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 com.android.devicelockcontroller.policy; 18 19 import static com.android.devicelockcontroller.activities.ProvisioningActivity.EXTRA_SHOW_CRITICAL_PROVISION_FAILED_UI_ON_START; 20 import static com.android.devicelockcontroller.common.DeviceLockConstants.ACTION_START_DEVICE_FINANCING_PROVISIONING; 21 import static com.android.devicelockcontroller.common.DeviceLockConstants.ACTION_START_DEVICE_SUBSIDY_PROVISIONING; 22 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.CLEARED; 23 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.LOCKED; 24 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.UNDEFINED; 25 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.UNLOCKED; 26 import static com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionState.KIOSK_PROVISIONED; 27 import static com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionState.PROVISION_FAILED; 28 import static com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionState.PROVISION_IN_PROGRESS; 29 import static com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionState.PROVISION_PAUSED; 30 import static com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionState.PROVISION_SUCCEEDED; 31 import static com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionState.UNPROVISIONED; 32 import static com.android.devicelockcontroller.policy.StartLockTaskModeWorker.START_LOCK_TASK_MODE_WORK_NAME; 33 34 import android.app.admin.DevicePolicyManager; 35 import android.content.ComponentName; 36 import android.content.Context; 37 import android.content.Intent; 38 import android.content.pm.PackageManager; 39 import android.content.pm.ResolveInfo; 40 import android.database.sqlite.SQLiteException; 41 import android.os.Build; 42 import android.os.UserManager; 43 44 import androidx.annotation.GuardedBy; 45 import androidx.annotation.VisibleForTesting; 46 import androidx.work.ExistingWorkPolicy; 47 import androidx.work.OneTimeWorkRequest; 48 import androidx.work.Operation; 49 import androidx.work.OutOfQuotaPolicy; 50 import androidx.work.WorkManager; 51 52 import com.android.devicelockcontroller.SystemDeviceLockManager; 53 import com.android.devicelockcontroller.activities.LandingActivity; 54 import com.android.devicelockcontroller.activities.ProvisioningActivity; 55 import com.android.devicelockcontroller.common.DeviceLockConstants; 56 import com.android.devicelockcontroller.common.DeviceLockConstants.ProvisioningType; 57 import com.android.devicelockcontroller.policy.DeviceStateController.DeviceState; 58 import com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionState; 59 import com.android.devicelockcontroller.provision.worker.ReportDeviceProvisionStateWorker; 60 import com.android.devicelockcontroller.schedule.DeviceLockControllerScheduler; 61 import com.android.devicelockcontroller.schedule.DeviceLockControllerSchedulerProvider; 62 import com.android.devicelockcontroller.storage.GlobalParametersClient; 63 import com.android.devicelockcontroller.storage.SetupParametersClient; 64 import com.android.devicelockcontroller.util.LogUtil; 65 66 import com.google.common.util.concurrent.FutureCallback; 67 import com.google.common.util.concurrent.Futures; 68 import com.google.common.util.concurrent.ListenableFuture; 69 import com.google.common.util.concurrent.MoreExecutors; 70 71 import java.util.ArrayList; 72 import java.util.List; 73 import java.util.concurrent.Executor; 74 75 /** 76 * An implementation of {@link DevicePolicyController}. This class guarantees thread safety by 77 * synchronizing policies enforcement on background threads in the order of when the API calls 78 * happen. That is, a pre-exist enforcement request will always blocks a incoming enforcement 79 * request until the former completes. 80 */ 81 public final class DevicePolicyControllerImpl implements DevicePolicyController { 82 private static final String TAG = "DevicePolicyControllerImpl"; 83 84 private final List<PolicyHandler> mPolicyList = new ArrayList<>(); 85 private final Context mContext; 86 private final DevicePolicyManager mDpm; 87 private final ProvisionStateController mProvisionStateController; 88 // A future that returns the current lock task type for the current provision/device state 89 // after policies enforcement are done. 90 @GuardedBy("this") 91 private ListenableFuture<@LockTaskType Integer> mCurrentEnforcedLockTaskTypeFuture = 92 Futures.immediateFuture(LockTaskType.UNDEFINED); 93 private final Executor mBgExecutor; 94 static final String ACTION_DEVICE_LOCK_KIOSK_SETUP = 95 "com.android.devicelock.action.KIOSK_SETUP"; 96 private static final String DEVICE_LOCK_VERSION_EXTRA = "DEVICE_LOCK_VERSION"; 97 private static final int DEVICE_LOCK_VERSION = 2; 98 private final UserManager mUserManager; 99 100 /** 101 * Create a new policy controller. 102 * 103 * @param context The context used by this policy controller. 104 * @param devicePolicyManager The device policy manager. 105 * @param userManager The user manager. 106 * @param systemDeviceLockManager The system device lock manager. 107 * @param provisionStateController The provision state controller. 108 * @param bgExecutor The background executor. 109 */ DevicePolicyControllerImpl(Context context, DevicePolicyManager devicePolicyManager, UserManager userManager, SystemDeviceLockManager systemDeviceLockManager, ProvisionStateController provisionStateController, Executor bgExecutor)110 public DevicePolicyControllerImpl(Context context, 111 DevicePolicyManager devicePolicyManager, 112 UserManager userManager, 113 SystemDeviceLockManager systemDeviceLockManager, 114 ProvisionStateController provisionStateController, 115 Executor bgExecutor) { 116 this(context, 117 devicePolicyManager, 118 userManager, 119 new UserRestrictionsPolicyHandler(devicePolicyManager, userManager, 120 Build.isDebuggable(), 121 bgExecutor), 122 new AppOpsPolicyHandler(systemDeviceLockManager, bgExecutor), 123 new LockTaskModePolicyHandler(context, devicePolicyManager, bgExecutor), 124 new PackagePolicyHandler(context, devicePolicyManager, bgExecutor), 125 new RolePolicyHandler(systemDeviceLockManager, bgExecutor), 126 new KioskKeepAlivePolicyHandler(systemDeviceLockManager, bgExecutor), 127 new ControllerKeepAlivePolicyHandler(systemDeviceLockManager, bgExecutor), 128 new NotificationsPolicyHandler(systemDeviceLockManager, bgExecutor), 129 provisionStateController, 130 bgExecutor); 131 } 132 133 @VisibleForTesting DevicePolicyControllerImpl(Context context, DevicePolicyManager devicePolicyManager, UserManager userManager, UserRestrictionsPolicyHandler userRestrictionsPolicyHandler, AppOpsPolicyHandler appOpsPolicyHandler, LockTaskModePolicyHandler lockTaskModePolicyHandler, PackagePolicyHandler packagePolicyHandler, RolePolicyHandler rolePolicyHandler, KioskKeepAlivePolicyHandler kioskKeepAlivePolicyHandler, ControllerKeepAlivePolicyHandler controllerKeepAlivePolicyHandler, NotificationsPolicyHandler notificationsPolicyHandler, ProvisionStateController provisionStateController, Executor bgExecutor)134 DevicePolicyControllerImpl(Context context, 135 DevicePolicyManager devicePolicyManager, 136 UserManager userManager, 137 UserRestrictionsPolicyHandler userRestrictionsPolicyHandler, 138 AppOpsPolicyHandler appOpsPolicyHandler, 139 LockTaskModePolicyHandler lockTaskModePolicyHandler, 140 PackagePolicyHandler packagePolicyHandler, 141 RolePolicyHandler rolePolicyHandler, 142 KioskKeepAlivePolicyHandler kioskKeepAlivePolicyHandler, 143 ControllerKeepAlivePolicyHandler controllerKeepAlivePolicyHandler, 144 NotificationsPolicyHandler notificationsPolicyHandler, 145 ProvisionStateController provisionStateController, 146 Executor bgExecutor) { 147 mContext = context; 148 mProvisionStateController = provisionStateController; 149 mBgExecutor = bgExecutor; 150 mDpm = devicePolicyManager; 151 mUserManager = userManager; 152 mPolicyList.add(userRestrictionsPolicyHandler); 153 mPolicyList.add(appOpsPolicyHandler); 154 mPolicyList.add(lockTaskModePolicyHandler); 155 mPolicyList.add(packagePolicyHandler); 156 mPolicyList.add(rolePolicyHandler); 157 mPolicyList.add(kioskKeepAlivePolicyHandler); 158 mPolicyList.add(controllerKeepAlivePolicyHandler); 159 mPolicyList.add(notificationsPolicyHandler); 160 } 161 162 @Override wipeDevice()163 public boolean wipeDevice() { 164 LogUtil.i(TAG, "Wiping device"); 165 try { 166 mDpm.wipeDevice(DevicePolicyManager.WIPE_SILENTLY 167 | DevicePolicyManager.WIPE_RESET_PROTECTION_DATA); 168 } catch (SecurityException e) { 169 LogUtil.e(TAG, "Cannot wipe device", e); 170 return false; 171 } 172 return true; 173 } 174 175 @Override enforceCurrentPolicies()176 public ListenableFuture<Void> enforceCurrentPolicies() { 177 return Futures.transform(enforceCurrentPoliciesAndResolveLockTaskType( 178 /* failure= */ false), 179 mode -> { 180 startLockTaskModeIfNeeded(mode); 181 return null; 182 }, 183 mBgExecutor); 184 } 185 186 @Override enforceCurrentPoliciesForCriticalFailure()187 public ListenableFuture<Void> enforceCurrentPoliciesForCriticalFailure() { 188 return Futures.transform(enforceCurrentPoliciesAndResolveLockTaskType( 189 /* failure= */ true), 190 mode -> { 191 startLockTaskModeIfNeeded(mode); 192 handlePolicyEnforcementFailure(); 193 return null; 194 }, 195 mBgExecutor); 196 } 197 198 private void handlePolicyEnforcementFailure() { 199 final DeviceLockControllerSchedulerProvider schedulerProvider = 200 (DeviceLockControllerSchedulerProvider) mContext.getApplicationContext(); 201 final DeviceLockControllerScheduler scheduler = 202 schedulerProvider.getDeviceLockControllerScheduler(); 203 // Hard failure due to policy enforcement, treat it as mandatory reset device alarm. 204 scheduler.scheduleMandatoryResetDeviceAlarm(); 205 206 ReportDeviceProvisionStateWorker.reportSetupFailed(WorkManager.getInstance(mContext), 207 DeviceLockConstants.ProvisionFailureReason.POLICY_ENFORCEMENT_FAILED); 208 } 209 210 /** 211 * Enforce current policies and then return the resulting lock task type 212 * 213 * @param failure true if this enforcement is due to resetting policies in case of failure. 214 * @return A future for the lock task type corresponding to the current policies. 215 */ 216 private ListenableFuture<@LockTaskType Integer> enforceCurrentPoliciesAndResolveLockTaskType( 217 boolean failure) { 218 synchronized (this) { 219 // current lock task type must be assigned to a local variable; otherwise, if 220 // retrieved down the execution flow, it will be returning the new type after execution. 221 ListenableFuture<@LockTaskType Integer> currentLockTaskType = 222 mCurrentEnforcedLockTaskTypeFuture; 223 ListenableFuture<@LockTaskType Integer> policiesEnforcementFuture = 224 Futures.transformAsync( 225 currentLockTaskType, 226 unused -> { 227 final ListenableFuture<@ProvisionState Integer> provisionState = 228 mProvisionStateController.getState(); 229 final ListenableFuture<@DeviceState Integer> deviceState = 230 GlobalParametersClient.getInstance().getDeviceState(); 231 return Futures.whenAllSucceed(provisionState, deviceState) 232 .callAsync( 233 () -> enforcePoliciesForCurrentStates( 234 Futures.getDone(provisionState), 235 Futures.getDone(deviceState)), 236 mBgExecutor 237 ); 238 }, 239 mBgExecutor); 240 if (failure) { 241 mCurrentEnforcedLockTaskTypeFuture = Futures.immediateFuture( 242 LockTaskType.CRITICAL_ERROR); 243 return mCurrentEnforcedLockTaskTypeFuture; 244 } else { 245 // To prevent exception propagate to future policies enforcement, catch any 246 // exceptions that might happen during the execution and fallback to previous type 247 // if exception happens. 248 mCurrentEnforcedLockTaskTypeFuture = Futures.catchingAsync( 249 policiesEnforcementFuture, 250 Exception.class, unused -> currentLockTaskType, 251 MoreExecutors.directExecutor()); 252 } 253 return policiesEnforcementFuture; 254 } 255 } 256 257 private ListenableFuture<@LockTaskType Integer> enforcePoliciesForCurrentStates( 258 @ProvisionState int provisionState, @DeviceState int deviceState) { 259 LogUtil.i(TAG, "Enforcing policies for provision state " + provisionState 260 + " and device state " + deviceState); 261 if (provisionState == UNPROVISIONED) { 262 return Futures.immediateFuture(resolveLockTaskType(provisionState, deviceState)); 263 } 264 List<ListenableFuture<Boolean>> futures = new ArrayList<>(); 265 if (deviceState == CLEARED) { 266 // If device is cleared, then ignore provision state and add cleared policies 267 for (int i = 0, policyLen = mPolicyList.size(); i < policyLen; i++) { 268 PolicyHandler policy = mPolicyList.get(i); 269 futures.add(policy.onCleared()); 270 } 271 } else if (provisionState == PROVISION_SUCCEEDED) { 272 // If provisioning has succeeded, then ignore provision state and add device state 273 // policies 274 for (int i = 0, policyLen = mPolicyList.size(); i < policyLen; i++) { 275 PolicyHandler policy = mPolicyList.get(i); 276 switch (deviceState) { 277 case UNLOCKED: 278 futures.add(policy.onUnlocked()); 279 break; 280 case LOCKED: 281 futures.add(policy.onLocked()); 282 break; 283 case UNDEFINED: 284 // No policies to enforce in this state. 285 break; 286 default: 287 throw new IllegalArgumentException( 288 "Invalid device state to enforce: " + deviceState); 289 } 290 } 291 } else { 292 for (int i = 0, policyLen = mPolicyList.size(); i < policyLen; i++) { 293 PolicyHandler policy = mPolicyList.get(i); 294 switch (provisionState) { 295 case PROVISION_IN_PROGRESS: 296 futures.add(policy.onProvisionInProgress()); 297 break; 298 case KIOSK_PROVISIONED: 299 futures.add(policy.onProvisioned()); 300 break; 301 case PROVISION_PAUSED: 302 futures.add(policy.onProvisionPaused()); 303 break; 304 case PROVISION_FAILED: 305 futures.add(policy.onProvisionFailed()); 306 break; 307 default: 308 throw new IllegalArgumentException( 309 "Invalid provision state to enforce: " + provisionState); 310 } 311 } 312 } 313 return Futures.transform(Futures.allAsList(futures), 314 results -> { 315 if (results.stream().reduce(true, (a, r) -> a && r)) { 316 return resolveLockTaskType(provisionState, deviceState); 317 } else { 318 throw new IllegalStateException( 319 "Failed to enforce policies for provision state " + provisionState 320 + " and device state " + deviceState); 321 } 322 }, 323 MoreExecutors.directExecutor()); 324 } 325 326 /** 327 * Determines the lock task type based on the current provision and device state 328 */ 329 private @LockTaskType int resolveLockTaskType(int provisionState, int deviceState) { 330 if (provisionState == UNPROVISIONED || deviceState == CLEARED) { 331 return LockTaskType.NOT_IN_LOCK_TASK; 332 } 333 if (provisionState == PROVISION_IN_PROGRESS) { 334 return LockTaskType.LANDING_ACTIVITY; 335 } 336 if (provisionState == KIOSK_PROVISIONED) { 337 return LockTaskType.KIOSK_SETUP_ACTIVITY; 338 } 339 if (provisionState == PROVISION_SUCCEEDED && deviceState == LOCKED) { 340 return LockTaskType.KIOSK_LOCK_ACTIVITY; 341 } 342 return LockTaskType.NOT_IN_LOCK_TASK; 343 } 344 345 private ListenableFuture<Intent> getLockScreenActivityIntent() { 346 return Futures.transform( 347 SetupParametersClient.getInstance().getKioskPackage(), 348 kioskPackage -> { 349 if (kioskPackage == null) { 350 throw new IllegalStateException("Missing kiosk package parameter!"); 351 } 352 Intent homeIntent = new Intent(Intent.ACTION_MAIN) 353 .addCategory(Intent.CATEGORY_HOME) 354 .setPackage(kioskPackage); 355 PackageManager pm = mContext.getPackageManager(); 356 ResolveInfo resolvedInfo = pm.resolveActivity(homeIntent, 357 PackageManager.MATCH_DEFAULT_ONLY); 358 if (resolvedInfo != null && resolvedInfo.activityInfo != null) { 359 return homeIntent.setComponent( 360 new ComponentName(kioskPackage, 361 resolvedInfo.activityInfo.name)); 362 } 363 // Kiosk app does not have an activity to handle the default 364 // home intent. Fall back to the launch activity. 365 // Note that in this case, Kiosk App can't be effectively set as 366 // the default home activity. 367 Intent launchIntent = pm.getLaunchIntentForPackage(kioskPackage); 368 if (launchIntent == null) { 369 throw new IllegalStateException( 370 "Failed to get launch intent for kiosk app!"); 371 } 372 return launchIntent; 373 }, mBgExecutor); 374 } 375 376 private ListenableFuture<Intent> getLandingActivityIntent() { 377 SetupParametersClient client = SetupParametersClient.getInstance(); 378 ListenableFuture<@ProvisioningType Integer> provisioningType = 379 client.getProvisioningType(); 380 return Futures.transform(provisioningType, 381 type -> { 382 Intent resultIntent = new Intent(mContext, LandingActivity.class); 383 switch (type) { 384 case ProvisioningType.TYPE_FINANCED: 385 // TODO(b/288923554) this used to return an intent with action 386 // ACTION_START_DEVICE_FINANCING_SECONDARY_USER_PROVISIONING 387 // for secondary users. Rework once a decision has been made about 388 // what to show to users. 389 return resultIntent.setAction( 390 ACTION_START_DEVICE_FINANCING_PROVISIONING); 391 case ProvisioningType.TYPE_SUBSIDY: 392 return resultIntent.setAction(ACTION_START_DEVICE_SUBSIDY_PROVISIONING); 393 case ProvisioningType.TYPE_UNDEFINED: 394 default: 395 throw new IllegalArgumentException("Provisioning type is unknown!"); 396 } 397 }, mBgExecutor); 398 } 399 400 private ListenableFuture<Intent> getKioskSetupActivityIntent() { 401 return Futures.transform(SetupParametersClient.getInstance().getKioskPackage(), 402 kioskPackageName -> { 403 if (kioskPackageName == null) { 404 throw new IllegalStateException("Missing kiosk package parameter!"); 405 } 406 final Intent kioskSetupIntent = new Intent(ACTION_DEVICE_LOCK_KIOSK_SETUP); 407 kioskSetupIntent.setPackage(kioskPackageName); 408 final ResolveInfo resolveInfo = mContext.getPackageManager() 409 .resolveActivity(kioskSetupIntent, PackageManager.MATCH_DEFAULT_ONLY); 410 if (resolveInfo == null || resolveInfo.activityInfo == null) { 411 throw new IllegalStateException( 412 "Failed to get setup activity intent for kiosk app!"); 413 } 414 kioskSetupIntent.putExtra(DEVICE_LOCK_VERSION_EXTRA, DEVICE_LOCK_VERSION); 415 return kioskSetupIntent.setComponent(new ComponentName(kioskPackageName, 416 resolveInfo.activityInfo.name)); 417 }, mBgExecutor); 418 } 419 420 private ListenableFuture<Intent> getProvisioningActivityIntentForCriticalFailure() { 421 final Intent intent = new Intent(mContext, ProvisioningActivity.class) 422 .putExtra(EXTRA_SHOW_CRITICAL_PROVISION_FAILED_UI_ON_START, true); 423 return Futures.immediateFuture(intent); 424 } 425 426 427 @Override 428 public ListenableFuture<Intent> getLaunchIntentForCurrentState() { 429 return Futures.transformAsync(getCurrentEnforcedLockTaskType(), 430 type -> { 431 switch (type) { 432 case LockTaskType.NOT_IN_LOCK_TASK: 433 return Futures.immediateFuture(null); 434 case LockTaskType.LANDING_ACTIVITY: 435 return getLandingActivityIntent(); 436 case LockTaskType.CRITICAL_ERROR: 437 return getProvisioningActivityIntentForCriticalFailure(); 438 case LockTaskType.KIOSK_SETUP_ACTIVITY: 439 return getKioskSetupActivityIntent(); 440 case LockTaskType.KIOSK_LOCK_ACTIVITY: 441 return getLockScreenActivityIntent(); 442 default: 443 throw new IllegalArgumentException("Invalid lock task type!"); 444 } 445 }, mBgExecutor); 446 } 447 448 /** 449 * Gets the currently enforced lock task type, enforcing current policies if they haven't been 450 * enforced yet. 451 */ 452 private ListenableFuture<@LockTaskType Integer> getCurrentEnforcedLockTaskType() { 453 synchronized (this) { 454 return Futures.transformAsync( 455 mCurrentEnforcedLockTaskTypeFuture, 456 type -> type == LockTaskType.UNDEFINED 457 ? Futures.transform(enforceCurrentPoliciesAndResolveLockTaskType( 458 /* failure= */ false), 459 mode -> { 460 startLockTaskModeIfNeeded(mode); 461 return mode; 462 }, mBgExecutor) 463 : Futures.immediateFuture(type), 464 mBgExecutor); 465 } 466 } 467 468 @Override 469 public ListenableFuture<Void> onUserUnlocked() { 470 return Futures.transformAsync(mProvisionStateController.onUserUnlocked(), 471 unused -> Futures.transform(getCurrentEnforcedLockTaskType(), 472 mode -> { 473 startLockTaskModeIfNeeded(mode); 474 return null; 475 }, 476 mBgExecutor), 477 mBgExecutor); 478 } 479 480 @Override 481 public ListenableFuture<Void> onUserSetupCompleted() { 482 return mProvisionStateController.onUserSetupCompleted(); 483 } 484 485 @Override 486 public ListenableFuture<Void> onAppCrashed(boolean isKiosk) { 487 final String crashedApp = isKiosk ? "kiosk" : "dlc"; 488 LogUtil.i(TAG, "Controller notified about " + crashedApp 489 + " having crashed while in lock task mode"); 490 return Futures.transform(getCurrentEnforcedLockTaskType(), 491 mode -> { 492 startLockTaskModeIfNeeded(mode); 493 return null; 494 }, 495 mBgExecutor); 496 } 497 498 private void startLockTaskModeIfNeeded(@LockTaskType Integer type) { 499 if (type == LockTaskType.NOT_IN_LOCK_TASK || !mUserManager.isUserUnlocked()) { 500 return; 501 } 502 WorkManager workManager = WorkManager.getInstance(mContext); 503 OneTimeWorkRequest startLockTask = new OneTimeWorkRequest.Builder( 504 StartLockTaskModeWorker.class) 505 .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) 506 .build(); 507 final ListenableFuture<Operation.State.SUCCESS> enqueueResult = 508 workManager.enqueueUniqueWork(START_LOCK_TASK_MODE_WORK_NAME, 509 ExistingWorkPolicy.REPLACE, startLockTask).getResult(); 510 Futures.addCallback(enqueueResult, new FutureCallback<>() { 511 @Override 512 public void onSuccess(Operation.State.SUCCESS result) { 513 // Enqueued 514 } 515 516 @Override 517 public void onFailure(Throwable t) { 518 LogUtil.e(TAG, "Failed to enqueue 'start lock task mode' work", t); 519 if (t instanceof SQLiteException) { 520 wipeDevice(); 521 } else { 522 LogUtil.e(TAG, "Not wiping device (non SQL exception)"); 523 } 524 } 525 }, mBgExecutor); 526 } 527 } 528