• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 android.devicelock;
18 
19 import android.Manifest.permission;
20 import android.annotation.CallbackExecutor;
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.RequiresFeature;
24 import android.annotation.RequiresNoPermission;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SystemService;
27 import android.content.Context;
28 import android.content.pm.PackageManager;
29 import android.os.OutcomeReceiver;
30 import android.os.RemoteException;
31 import android.text.TextUtils;
32 
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.Map;
36 import java.util.Objects;
37 import java.util.concurrent.Executor;
38 
39 /**
40  * Manager used to interact with the system device lock service.
41  * The device lock feature is used by special applications ('kiosk apps', downloaded and installed
42  * by the device lock solution) to lock and unlock a device.
43  * A typical use case is a financed device, where the financing entity has the capability to lock
44  * the device in case of a missed payment.
45  * When a device is locked, only a limited set of interactions with the device is allowed (for
46  * example, placing emergency calls).
47  * <p>
48  * Use {@link android.content.Context#getSystemService(java.lang.String)}
49  * with {@link Context#DEVICE_LOCK_SERVICE} to create a {@link DeviceLockManager}.
50  * </p>
51  *
52  */
53 @SystemService(Context.DEVICE_LOCK_SERVICE)
54 @RequiresFeature(PackageManager.FEATURE_DEVICE_LOCK)
55 public final class DeviceLockManager {
56     private static final String TAG = "DeviceLockManager";
57     private final IDeviceLockService mService;
58 
59     /** @hide */
60     @Retention(RetentionPolicy.SOURCE)
61     @IntDef(prefix = "DEVICE_LOCK_ROLE_", value = {
62         DEVICE_LOCK_ROLE_FINANCING,
63     })
64     public @interface DeviceLockRole {}
65 
66     /**
67      * Constant representing a financed device role, returned by {@link #getKioskApps}.
68      */
69     public static final int DEVICE_LOCK_ROLE_FINANCING = 0;
70 
71     /**
72      * @hide
73      */
DeviceLockManager(Context context, IDeviceLockService service)74     public DeviceLockManager(Context context, IDeviceLockService service) {
75         mService = service;
76     }
77 
78     /**
79      * Return the underlying service interface.
80      * This is used to implement private APIs between the Device Lock Controller and the
81      * Device Lock System Service.
82      *
83      * @hide
84      */
85     @NonNull
getService()86     public IDeviceLockService getService() {
87         return mService;
88     }
89 
90     /**
91      * Lock the device.
92      *
93      * @param executor the {@link Executor} on which to invoke the callback.
94      * @param callback this returns either success or an exception.
95      */
96     @RequiresPermission(permission.MANAGE_DEVICE_LOCK_STATE)
lockDevice(@onNull @allbackExecutor Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)97     public void lockDevice(@NonNull @CallbackExecutor Executor executor,
98             @NonNull OutcomeReceiver<Void, Exception> callback) {
99         Objects.requireNonNull(executor);
100         Objects.requireNonNull(callback);
101 
102         try {
103             mService.lockDevice(
104                     new ILockUnlockDeviceCallback.Stub() {
105                         @Override
106                         public void onDeviceLockedUnlocked() {
107                             executor.execute(() -> callback.onResult(null));
108                         }
109 
110                         @Override
111                         public void onError(ParcelableException parcelableException) {
112                             callback.onError(parcelableException.getException());
113                         }
114                     });
115         } catch (RemoteException e) {
116             executor.execute(() -> callback.onError(new RuntimeException(e)));
117         }
118     }
119 
120     /**
121      * Unlock the device.
122      *
123      * @param executor the {@link Executor} on which to invoke the callback.
124      * @param callback this returns either success or an exception.
125      */
126     @RequiresPermission(permission.MANAGE_DEVICE_LOCK_STATE)
unlockDevice(@onNull @allbackExecutor Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)127     public void unlockDevice(@NonNull @CallbackExecutor Executor executor,
128             @NonNull OutcomeReceiver<Void, Exception> callback) {
129         Objects.requireNonNull(executor);
130         Objects.requireNonNull(callback);
131 
132         try {
133             mService.unlockDevice(
134                     new ILockUnlockDeviceCallback.Stub() {
135                         @Override
136                         public void onDeviceLockedUnlocked() {
137                             executor.execute(() -> callback.onResult(null));
138                         }
139 
140                         @Override
141                         public void onError(ParcelableException parcelableException) {
142                             callback.onError(parcelableException.getException());
143                         }
144                     });
145         } catch (RemoteException e) {
146             executor.execute(() -> callback.onError(new RuntimeException(e)));
147         }
148     }
149 
150     /**
151      * Check if the device is locked or not.
152      *
153      * @param executor the {@link Executor} on which to invoke the callback.
154      * @param callback this returns either the lock status or an exception.
155      */
156     @RequiresPermission(permission.MANAGE_DEVICE_LOCK_STATE)
isDeviceLocked(@onNull @allbackExecutor Executor executor, @NonNull OutcomeReceiver<Boolean, Exception> callback)157     public void isDeviceLocked(@NonNull @CallbackExecutor Executor executor,
158             @NonNull OutcomeReceiver<Boolean, Exception> callback) {
159         Objects.requireNonNull(executor);
160         Objects.requireNonNull(callback);
161 
162         try {
163             mService.isDeviceLocked(
164                     new IIsDeviceLockedCallback.Stub() {
165                         @Override
166                         public void onIsDeviceLocked(boolean locked) {
167                             executor.execute(() -> callback.onResult(locked));
168                         }
169 
170                         @Override
171                         public void onError(ParcelableException parcelableException) {
172                             executor.execute(() ->
173                                     callback.onError(parcelableException.getException()));
174                         }
175                     });
176         } catch (RemoteException e) {
177             executor.execute(() -> callback.onError(new RuntimeException(e)));
178         }
179     }
180 
181     /**
182      * Get the device id.
183      *
184      * @param executor the {@link Executor} on which to invoke the callback.
185      * @param callback this returns either the {@link DeviceId} or an exception.
186      */
187     @RequiresPermission(permission.MANAGE_DEVICE_LOCK_STATE)
getDeviceId(@onNull @allbackExecutor Executor executor, @NonNull OutcomeReceiver<DeviceId, Exception> callback)188     public void getDeviceId(@NonNull @CallbackExecutor Executor executor,
189             @NonNull OutcomeReceiver<DeviceId, Exception> callback) {
190         Objects.requireNonNull(executor);
191         Objects.requireNonNull(callback);
192 
193         try {
194             mService.getDeviceId(
195                     new IGetDeviceIdCallback.Stub() {
196                         @Override
197                         public void onDeviceIdReceived(int type, String id) {
198                             if (TextUtils.isEmpty(id)) {
199                                 executor.execute(() -> {
200                                     callback.onError(new Exception("Cannot get device id (empty)"));
201                                 });
202                             } else {
203                                 executor.execute(() -> {
204                                     callback.onResult(new DeviceId(type, id));
205                                 });
206                             }
207                         }
208 
209                         @Override
210                         public void onError(ParcelableException parcelableException) {
211                             callback.onError(parcelableException.getException());
212                         }
213                     }
214             );
215         } catch (RemoteException e) {
216             executor.execute(() -> callback.onError(new RuntimeException(e)));
217         }
218     }
219 
220     /**
221      * Get the kiosk app roles and packages.
222      *
223      * @param executor the {@link Executor} on which to invoke the callback.
224      * @param callback this returns either a {@link Map} of device roles/package names,
225      *                 or an exception. The Integer in the map represent the device lock role
226      *                 (at this moment, the only supported role is
227      *                 {@value #DEVICE_LOCK_ROLE_FINANCING}. The String represents tha package
228      *                 name of the kiosk app for that role.
229      */
230     @RequiresNoPermission
getKioskApps(@onNull @allbackExecutor Executor executor, @NonNull OutcomeReceiver<Map<Integer, String>, Exception> callback)231     public void getKioskApps(@NonNull @CallbackExecutor Executor executor,
232             @NonNull OutcomeReceiver<Map<Integer, String>, Exception> callback) {
233         Objects.requireNonNull(executor);
234         Objects.requireNonNull(callback);
235 
236         try {
237             mService.getKioskApps(
238                     new IGetKioskAppsCallback.Stub() {
239                         @Override
240                         public void onKioskAppsReceived(Map kioskApps) {
241                             executor.execute(() -> callback.onResult(kioskApps));
242                         }
243 
244                         @Override
245                         public void onError(ParcelableException parcelableException) {
246                             callback.onError(parcelableException.getException());
247                         }
248                     }
249             );
250         } catch (RemoteException e) {
251             executor.execute(() -> callback.onError(new RuntimeException(e)));
252         }
253     }
254 }
255