1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.devicelock;
18 
19 import android.annotation.IntDef;
20 import android.os.OutcomeReceiver;
21 
22 import com.android.internal.annotations.GuardedBy;
23 
24 import java.lang.annotation.ElementType;
25 import java.lang.annotation.Retention;
26 import java.lang.annotation.RetentionPolicy;
27 import java.lang.annotation.Target;
28 
29 /**
30  * Stub implementation of the connector that is used when the device has had its restrictions
31  * cleared so that we don't try to bind to a disabled package.
32  */
33 public class DeviceLockControllerConnectorStub implements DeviceLockControllerConnector {
34     // Pseudo states used for CTS conformance when a device is finalized.
35     @Target(ElementType.TYPE_USE)
36     @Retention(RetentionPolicy.SOURCE)
37     @IntDef({
38             DevicePseudoState.UNDEFINED,
39             DevicePseudoState.LOCKED,
40             DevicePseudoState.UNLOCKED,
41             DevicePseudoState.CLEARED,
42     })
43     private @interface DevicePseudoState {
44         int UNDEFINED = 0;
45         int UNLOCKED = 1;
46         int LOCKED = 2;
47         int CLEARED = 3;
48     }
49 
50     @GuardedBy("this")
51     private @DevicePseudoState int mPseudoState = DevicePseudoState.UNDEFINED;
52 
53     @Override
unbind()54     public void unbind() {}
55 
56     @Override
lockDevice(OutcomeReceiver<Void, Exception> callback)57     public void lockDevice(OutcomeReceiver<Void, Exception> callback) {
58         synchronized (this) {
59             if (setExceptionIfDeviceIsCleared(callback)) {
60                 return;
61             }
62 
63             mPseudoState = DevicePseudoState.LOCKED;
64         }
65         callback.onResult(/* result= */ null);
66     }
67 
68     @Override
unlockDevice(OutcomeReceiver<Void, Exception> callback)69     public void unlockDevice(OutcomeReceiver<Void, Exception> callback) {
70         synchronized (this) {
71             if (setExceptionIfDeviceIsCleared(callback)) {
72                 return;
73             }
74 
75             mPseudoState = DevicePseudoState.UNLOCKED;
76         }
77         callback.onResult(/* result= */ null);
78     }
79 
80     @Override
isDeviceLocked(OutcomeReceiver<Boolean, Exception> callback)81     public void isDeviceLocked(OutcomeReceiver<Boolean, Exception> callback) {
82         boolean isLocked;
83 
84         synchronized (this) {
85             if (setExceptionIfDeviceIsCleared(callback)) {
86                 return;
87             }
88 
89             if (mPseudoState == DevicePseudoState.UNDEFINED) {
90                 setException(callback, "isLocked called before setting the lock state");
91 
92                 return;
93             }
94 
95             isLocked = mPseudoState == DevicePseudoState.LOCKED;
96         }
97 
98         callback.onResult(isLocked);
99     }
100 
101     @Override
getDeviceId(OutcomeReceiver<String, Exception> callback)102     public void getDeviceId(OutcomeReceiver<String, Exception> callback) {
103         setException(callback, "No registered Device ID found");
104     }
105 
106     @Override
clearDeviceRestrictions(OutcomeReceiver<Void, Exception> callback)107     public void clearDeviceRestrictions(OutcomeReceiver<Void, Exception> callback) {
108         synchronized (this) {
109             if (setExceptionIfDeviceIsCleared(callback)) {
110                 return;
111             }
112 
113             mPseudoState = DevicePseudoState.CLEARED;
114         }
115         callback.onResult(/* result= */ null);
116     }
117 
118     @Override
onUserSwitching(OutcomeReceiver<Void, Exception> callback)119     public void onUserSwitching(OutcomeReceiver<Void, Exception> callback) {
120         // Do not throw exception as we expect this to be called
121         callback.onResult(/* result= */ null);
122     }
123 
124     @Override
onUserUnlocked(OutcomeReceiver<Void, Exception> callback)125     public void onUserUnlocked(OutcomeReceiver<Void, Exception> callback) {
126         // Do not throw exception as we expect this to be called
127         callback.onResult(/* result= */ null);
128     }
129 
130     @Override
onUserSetupCompleted(OutcomeReceiver<Void, Exception> callback)131     public void onUserSetupCompleted(OutcomeReceiver<Void, Exception> callback) {
132         // Do not throw exception as we expect this to be called
133         callback.onResult(/* result= */ null);
134     }
135 
136     @Override
onAppCrashed(boolean isKiosk, OutcomeReceiver<Void, Exception> callback)137     public void onAppCrashed(boolean isKiosk, OutcomeReceiver<Void, Exception> callback) {
138         setException(callback, "Device lock controller package is disabled");
139     }
140 
setException(OutcomeReceiver<?, Exception> callback, String message)141     private static void setException(OutcomeReceiver<?, Exception> callback, String message) {
142         callback.onError(new IllegalStateException(message));
143     }
144 
145     @GuardedBy("this")
setExceptionIfDeviceIsCleared(OutcomeReceiver<?, Exception> callback)146     private boolean setExceptionIfDeviceIsCleared(OutcomeReceiver<?, Exception> callback) {
147         if (mPseudoState == DevicePseudoState.CLEARED) {
148             setException(callback, "Device has been cleared!");
149 
150             return true;
151         }
152 
153         return false;
154     }
155 }
156