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.devicelockcontroller; 18 19 import android.annotation.CallbackExecutor; 20 import android.content.Context; 21 import android.content.pm.PackageManager; 22 import android.content.pm.PackageManager.PackageInfoFlags; 23 import android.devicelock.DeviceLockManager; 24 import android.devicelock.IDeviceLockService; 25 import android.os.Bundle; 26 import android.os.Handler; 27 import android.os.Looper; 28 import android.os.OutcomeReceiver; 29 import android.os.RemoteCallback; 30 import android.os.RemoteException; 31 32 import androidx.annotation.NonNull; 33 34 import java.util.Objects; 35 import java.util.concurrent.Executor; 36 37 /** 38 * Implementation for SystemDeviceLockManager. 39 */ 40 public final class SystemDeviceLockManagerImpl implements SystemDeviceLockManager { 41 private static final String TAG = "SystemDeviceLockManagerImpl"; 42 43 private final IDeviceLockService mIDeviceLockService; 44 private final Context mContext; 45 SystemDeviceLockManagerImpl(Context context)46 private SystemDeviceLockManagerImpl(Context context) { 47 mContext = context; 48 final DeviceLockManager deviceLockManager = context.getSystemService( 49 DeviceLockManager.class); 50 51 mIDeviceLockService = deviceLockManager.getService(); 52 } 53 SystemDeviceLockManagerImpl()54 private SystemDeviceLockManagerImpl() { 55 this(DeviceLockControllerApplication.getAppContext()); 56 } 57 58 // Initialization-on-demand holder. 59 private static final class SystemDeviceLockManagerHolder { 60 static final SystemDeviceLockManagerImpl sSystemDeviceLockManager = 61 new SystemDeviceLockManagerImpl(); 62 } 63 64 /** 65 * Returns the lazily initialized singleton instance of the SystemDeviceLockManager. 66 */ getInstance()67 public static SystemDeviceLockManager getInstance() { 68 return SystemDeviceLockManagerHolder.sSystemDeviceLockManager; 69 } 70 71 @Override addFinancedDeviceKioskRole(@onNull String packageName, @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)72 public void addFinancedDeviceKioskRole(@NonNull String packageName, 73 @CallbackExecutor Executor executor, 74 @NonNull OutcomeReceiver<Void, Exception> callback) { 75 Objects.requireNonNull(executor); 76 Objects.requireNonNull(callback); 77 78 try { 79 mIDeviceLockService.addFinancedDeviceKioskRole(packageName, 80 new RemoteCallback(result -> executor.execute(() -> { 81 processResult(result, callback, 82 "Failed to add financed role to: " + packageName); 83 }), new Handler(Looper.getMainLooper()))); 84 } catch (RemoteException e) { 85 executor.execute(() -> callback.onError(new RuntimeException(e))); 86 } 87 } 88 89 @Override removeFinancedDeviceKioskRole(@onNull String packageName, @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)90 public void removeFinancedDeviceKioskRole(@NonNull String packageName, 91 @CallbackExecutor Executor executor, 92 @NonNull OutcomeReceiver<Void, Exception> callback) { 93 Objects.requireNonNull(executor); 94 Objects.requireNonNull(callback); 95 96 try { 97 mIDeviceLockService.removeFinancedDeviceKioskRole(packageName, 98 new RemoteCallback(result -> executor.execute(() -> { 99 processResult(result, callback, 100 "Failed to remove financed role from: " + packageName); 101 }), new Handler(Looper.getMainLooper()))); 102 } catch (RemoteException e) { 103 executor.execute(() -> callback.onError(new RuntimeException(e))); 104 } 105 } 106 107 @Override setDlcExemptFromActivityBgStartRestrictionState(boolean exempt, @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)108 public void setDlcExemptFromActivityBgStartRestrictionState(boolean exempt, 109 @CallbackExecutor Executor executor, 110 @NonNull OutcomeReceiver<Void, Exception> callback) { 111 Objects.requireNonNull(executor); 112 Objects.requireNonNull(callback); 113 114 try { 115 mIDeviceLockService.setCallerExemptFromActivityBgStartRestrictionState(exempt, 116 new RemoteCallback(result -> executor.execute(() -> { 117 processResult(result, callback, 118 "Failed to change exempt from activity background start to: " 119 + (exempt ? "exempt" : "non exempt")); 120 }), new Handler(Looper.getMainLooper()))); 121 } catch (RemoteException e) { 122 executor.execute(() -> callback.onError(new RuntimeException(e))); 123 } 124 } 125 126 @Override setDlcAllowedToSendUndismissibleNotifications(boolean allowed, @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)127 public void setDlcAllowedToSendUndismissibleNotifications(boolean allowed, 128 @CallbackExecutor Executor executor, 129 @NonNull OutcomeReceiver<Void, Exception> callback) { 130 Objects.requireNonNull(executor); 131 Objects.requireNonNull(callback); 132 133 try { 134 mIDeviceLockService.setCallerAllowedToSendUndismissibleNotifications(allowed, 135 new RemoteCallback(result -> executor.execute(() -> { 136 processResult(result, callback, 137 "Failed to change undismissible notifs allowed to: " 138 + (allowed ? "allowed" : "not allowed")); 139 }), new Handler(Looper.getMainLooper()))); 140 } catch (RemoteException e) { 141 executor.execute(() -> callback.onError(new RuntimeException(e))); 142 } 143 } 144 145 @Override setKioskAppExemptFromRestrictionsState(String packageName, boolean exempt, Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)146 public void setKioskAppExemptFromRestrictionsState(String packageName, boolean exempt, 147 Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback) { 148 Objects.requireNonNull(executor); 149 Objects.requireNonNull(callback); 150 151 try { 152 int kioskUid = mContext.getPackageManager().getPackageUid(packageName, 153 PackageInfoFlags.of(0)); 154 mIDeviceLockService.setUidExemptFromRestrictionsState(kioskUid, exempt, 155 new RemoteCallback(result -> executor.execute(() -> { 156 processResult(result, callback, "Failed to exempt for UID: " + kioskUid); 157 }), new Handler(Looper.getMainLooper()))); 158 } catch (RemoteException | PackageManager.NameNotFoundException e) { 159 executor.execute(() -> callback.onError(new RuntimeException(e))); 160 } 161 } 162 163 @Override enableKioskKeepalive(String packageName, Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)164 public void enableKioskKeepalive(String packageName, Executor executor, 165 @NonNull OutcomeReceiver<Void, Exception> callback) { 166 Objects.requireNonNull(packageName); 167 Objects.requireNonNull(executor); 168 Objects.requireNonNull(callback); 169 170 try { 171 mIDeviceLockService.enableKioskKeepalive(packageName, 172 new RemoteCallback(result -> executor.execute(() -> { 173 processResult(result, callback, 174 "Failed to enable kiosk keep-alive for package: " 175 + packageName); 176 }), new Handler(Looper.getMainLooper()))); 177 } catch (RemoteException e) { 178 executor.execute(() -> callback.onError(new RuntimeException(e))); 179 } 180 } 181 182 @Override disableKioskKeepalive(Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)183 public void disableKioskKeepalive(Executor executor, 184 @NonNull OutcomeReceiver<Void, Exception> callback) { 185 Objects.requireNonNull(executor); 186 Objects.requireNonNull(callback); 187 188 try { 189 mIDeviceLockService.disableKioskKeepalive( 190 new RemoteCallback(result -> executor.execute(() -> { 191 processResult(result, callback, "Failed to disable kiosk keep-alive"); 192 }), new Handler(Looper.getMainLooper()))); 193 } catch (RemoteException e) { 194 executor.execute(() -> callback.onError(new RuntimeException(e))); 195 } 196 } 197 198 @Override enableControllerKeepalive(Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)199 public void enableControllerKeepalive(Executor executor, 200 @NonNull OutcomeReceiver<Void, Exception> callback) { 201 Objects.requireNonNull(executor); 202 Objects.requireNonNull(callback); 203 204 try { 205 mIDeviceLockService.enableControllerKeepalive( 206 new RemoteCallback(result -> executor.execute(() -> { 207 processResult(result, callback, "Failed to enable controller keep-alive"); 208 }), new Handler(Looper.getMainLooper()))); 209 } catch (RemoteException e) { 210 executor.execute(() -> callback.onError(new RuntimeException(e))); 211 } 212 } 213 214 @Override disableControllerKeepalive(Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)215 public void disableControllerKeepalive(Executor executor, 216 @NonNull OutcomeReceiver<Void, Exception> callback) { 217 Objects.requireNonNull(executor); 218 Objects.requireNonNull(callback); 219 220 try { 221 mIDeviceLockService.disableControllerKeepalive( 222 new RemoteCallback(result -> executor.execute(() -> { 223 processResult(result, callback, "Failed to disable controller keep-alive"); 224 }), new Handler(Looper.getMainLooper()))); 225 } catch (RemoteException e) { 226 executor.execute(() -> callback.onError(new RuntimeException(e))); 227 } 228 } 229 230 @Override setDeviceFinalized(boolean finalized, Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)231 public void setDeviceFinalized(boolean finalized, Executor executor, 232 @NonNull OutcomeReceiver<Void, Exception> callback) { 233 Objects.requireNonNull(executor); 234 Objects.requireNonNull(callback); 235 236 try { 237 mIDeviceLockService.setDeviceFinalized(finalized, 238 new RemoteCallback(result -> executor.execute(() -> { 239 processResult(result, callback, "Failed to set device finalized"); 240 }), new Handler(Looper.getMainLooper()))); 241 } catch (RemoteException e) { 242 executor.execute(() -> callback.onError(new RuntimeException(e))); 243 } 244 } 245 246 @Override setPostNotificationsSystemFixed(boolean systemFixed, Executor executor, @NonNull OutcomeReceiver<Void, Exception> callback)247 public void setPostNotificationsSystemFixed(boolean systemFixed, Executor executor, 248 @NonNull OutcomeReceiver<Void, Exception> callback) { 249 Objects.requireNonNull(executor); 250 Objects.requireNonNull(callback); 251 252 try { 253 mIDeviceLockService.setPostNotificationsSystemFixed(systemFixed, 254 new RemoteCallback(result -> executor.execute(() -> { 255 processResult(result, callback, "Failed to change POST_NOTIFICATIONS " 256 + "SYSTEM_FIXED flag to: " + systemFixed); 257 }), new Handler(Looper.getMainLooper()))); 258 } catch (RemoteException e) { 259 executor.execute(() -> callback.onError(new RuntimeException(e))); 260 } 261 } 262 processResult(Bundle result, @NonNull OutcomeReceiver<Void, Exception> callback, String message)263 private static void processResult(Bundle result, 264 @NonNull OutcomeReceiver<Void, Exception> callback, String message) { 265 final boolean remoteCallbackResult = result.getBoolean( 266 IDeviceLockService.KEY_REMOTE_CALLBACK_RESULT); 267 if (remoteCallbackResult) { 268 callback.onResult(null /* result */); 269 } else { 270 callback.onError(new Exception(message)); 271 } 272 } 273 } 274