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