1 /* 2 * Copyright 2014, 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.managedprovisioning; 18 19 import static android.app.admin.DeviceAdminReceiver.ACTION_PROFILE_PROVISIONING_COMPLETE; 20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; 21 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE; 22 23 import android.accounts.AccountManager; 24 import android.app.Activity; 25 import android.app.ActivityManagerNative; 26 import android.app.IActivityManager; 27 import android.app.Service; 28 import android.app.admin.DevicePolicyManager; 29 import android.content.BroadcastReceiver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.content.pm.IPackageManager; 34 import android.content.pm.PackageManager; 35 import android.content.pm.UserInfo; 36 import android.os.AsyncTask; 37 import android.os.IBinder; 38 import android.os.Process; 39 import android.os.RemoteException; 40 import android.os.ServiceManager; 41 import android.os.UserHandle; 42 import android.os.UserManager; 43 import android.provider.Settings; 44 import android.support.v4.content.LocalBroadcastManager; 45 46 import com.android.managedprovisioning.common.Utils; 47 import com.android.managedprovisioning.CrossProfileIntentFiltersHelper; 48 import com.android.managedprovisioning.model.ProvisioningParams; 49 import com.android.managedprovisioning.task.DeleteNonRequiredAppsTask; 50 import com.android.managedprovisioning.task.DisableBluetoothSharingTask; 51 import com.android.managedprovisioning.task.DisableInstallShortcutListenersTask; 52 import com.android.managedprovisioning.task.ManagedProfileSettingsTask; 53 54 import java.util.concurrent.Semaphore; 55 import java.util.concurrent.TimeUnit; 56 57 /** 58 * Service that runs the profile owner provisioning. 59 * 60 * <p>This service is started from and sends updates to the {@link ProfileOwnerProvisioningActivity}, 61 * which contains the provisioning UI. 62 */ 63 public class ProfileOwnerProvisioningService extends Service { 64 // Intent actions for communication with DeviceOwnerProvisioningService. 65 public static final String ACTION_PROVISIONING_SUCCESS = 66 "com.android.managedprovisioning.provisioning_success"; 67 public static final String ACTION_PROVISIONING_ERROR = 68 "com.android.managedprovisioning.error"; 69 public static final String ACTION_PROVISIONING_CANCELLED = 70 "com.android.managedprovisioning.cancelled"; 71 public static final String EXTRA_LOG_MESSAGE_KEY = "ProvisioningErrorLogMessage"; 72 73 // Maximum time we will wait for ACTION_USER_UNLOCK until we give up and continue without 74 // account migration. 75 private static final int USER_UNLOCKED_TIMEOUT_SECONDS = 120; // 2 minutes 76 77 // Status flags for the provisioning process. 78 /** Provisioning not started. */ 79 private static final int STATUS_UNKNOWN = 0; 80 /** Provisioning started, no errors or cancellation requested received. */ 81 private static final int STATUS_STARTED = 1; 82 /** Provisioning in progress, but user has requested cancellation. */ 83 private static final int STATUS_CANCELLING = 2; 84 // Final possible states for the provisioning process. 85 /** Provisioning completed successfully. */ 86 private static final int STATUS_DONE = 3; 87 /** Provisioning failed and cleanup complete. */ 88 private static final int STATUS_ERROR = 4; 89 /** Provisioning cancelled and cleanup complete. */ 90 private static final int STATUS_CANCELLED = 5; 91 92 private IPackageManager mIpm; 93 private UserInfo mManagedProfileOrUserInfo; 94 private AccountManager mAccountManager; 95 private UserManager mUserManager; 96 private UserUnlockedReceiver mUnlockedReceiver; 97 98 private AsyncTask<Intent, Void, Void> runnerTask; 99 100 // MessageId of the last error message. 101 private String mLastErrorMessage = null; 102 103 // Current status of the provisioning process. 104 private int mProvisioningStatus = STATUS_UNKNOWN; 105 106 private ProvisioningParams mParams; 107 108 private final Utils mUtils = new Utils(); 109 110 private class RunnerTask extends AsyncTask<Intent, Void, Void> { 111 @Override 112 protected Void doInBackground(Intent ... intents) { 113 // Atomically move to STATUS_STARTED at most once. 114 synchronized (ProfileOwnerProvisioningService.this) { 115 if (mProvisioningStatus == STATUS_UNKNOWN) { 116 mProvisioningStatus = STATUS_STARTED; 117 } else { 118 // Process already started, don't start again. 119 return null; 120 } 121 } 122 123 try { 124 initialize(intents[0]); 125 startManagedProfileOrUserProvisioning(); 126 } catch (ProvisioningException e) { 127 // Handle internal errors. 128 error(e.getMessage(), e); 129 finish(); 130 } catch (Exception e) { 131 // General catch-all to ensure process cleans up in all cases. 132 error("Failed to initialize managed profile, aborting.", e); 133 finish(); 134 } 135 136 return null; 137 } 138 } 139 140 @Override 141 public void onCreate() { 142 super.onCreate(); 143 144 mIpm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 145 mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE); 146 mUserManager = (UserManager) getSystemService(Context.USER_SERVICE); 147 148 runnerTask = new RunnerTask(); 149 } 150 151 @Override 152 public int onStartCommand(final Intent intent, int flags, int startId) { 153 if (ProfileOwnerProvisioningActivity.ACTION_CANCEL_PROVISIONING.equals(intent.getAction())) { 154 ProvisionLogger.logd("Cancelling profile owner provisioning service"); 155 cancelProvisioning(); 156 return START_NOT_STICKY; 157 } 158 159 ProvisionLogger.logd("Starting profile owner provisioning service"); 160 161 try { 162 runnerTask.execute(intent); 163 } catch (IllegalStateException e) { 164 // runnerTask is either in progress, or finished. 165 ProvisionLogger.logd( 166 "ProfileOwnerProvisioningService: Provisioning already started, " 167 + "second provisioning intent not being processed, only reporting status."); 168 reportStatus(); 169 } 170 return START_NOT_STICKY; 171 } 172 173 private void reportStatus() { 174 synchronized (this) { 175 switch (mProvisioningStatus) { 176 case STATUS_DONE: 177 notifyActivityOfSuccess(); 178 break; 179 case STATUS_CANCELLED: 180 notifyActivityCancelled(); 181 break; 182 case STATUS_ERROR: 183 notifyActivityError(); 184 break; 185 case STATUS_UNKNOWN: 186 case STATUS_STARTED: 187 case STATUS_CANCELLING: 188 // Don't notify UI of status when just-started/in-progress. 189 break; 190 } 191 } 192 } 193 194 private void cancelProvisioning() { 195 synchronized (this) { 196 switch (mProvisioningStatus) { 197 case STATUS_DONE: 198 // Process completed, we should honor user request to cancel 199 // though. 200 mProvisioningStatus = STATUS_CANCELLING; 201 cleanupUserProfile(); 202 mProvisioningStatus = STATUS_CANCELLED; 203 reportStatus(); 204 break; 205 case STATUS_UNKNOWN: 206 // Process hasn't started, move straight to cancelled state. 207 mProvisioningStatus = STATUS_CANCELLED; 208 reportStatus(); 209 break; 210 case STATUS_STARTED: 211 // Process is mid-flow, flag up that the user has requested 212 // cancellation. 213 mProvisioningStatus = STATUS_CANCELLING; 214 break; 215 case STATUS_CANCELLING: 216 // Cancellation already being processed. 217 break; 218 case STATUS_CANCELLED: 219 case STATUS_ERROR: 220 // Process already completed, nothing left to cancel. 221 break; 222 } 223 } 224 } 225 226 private void initialize(Intent intent) { 227 // Load the ProvisioningParams (from message in Intent). 228 mParams = (ProvisioningParams) intent.getParcelableExtra( 229 ProvisioningParams.EXTRA_PROVISIONING_PARAMS); 230 if (mParams.accountToMigrate != null) { 231 ProvisionLogger.logi("Migrating account to managed profile"); 232 } 233 } 234 235 /** 236 * This is the core method to create a managed profile or user. It goes through every 237 * provisioning step. 238 */ 239 private void startManagedProfileOrUserProvisioning() throws ProvisioningException { 240 241 ProvisionLogger.logd("Starting managed profile or user provisioning"); 242 243 if(isProvisioningManagedUser()) { 244 mManagedProfileOrUserInfo = mUserManager.getUserInfo(mUserManager.getUserHandle()); 245 if(mManagedProfileOrUserInfo == null) { 246 throw raiseError("Couldn't get current user information"); 247 } 248 } else { 249 // Work through the provisioning steps in their corresponding order 250 createProfile(getString(R.string.default_managed_profile_name)); 251 } 252 if (mManagedProfileOrUserInfo != null) { 253 final DeleteNonRequiredAppsTask deleteNonRequiredAppsTask; 254 final DisableInstallShortcutListenersTask disableInstallShortcutListenersTask; 255 final DisableBluetoothSharingTask disableBluetoothSharingTask; 256 final ManagedProfileSettingsTask managedProfileSettingsTask = 257 new ManagedProfileSettingsTask(this, mManagedProfileOrUserInfo.id); 258 259 disableInstallShortcutListenersTask = new DisableInstallShortcutListenersTask(this, 260 mManagedProfileOrUserInfo.id); 261 disableBluetoothSharingTask = new DisableBluetoothSharingTask( 262 mManagedProfileOrUserInfo.id); 263 // TODO Add separate set of apps for MANAGED_USER, currently same as of DEVICE_OWNER. 264 deleteNonRequiredAppsTask = new DeleteNonRequiredAppsTask(this, 265 mParams.deviceAdminComponentName.getPackageName(), 266 (isProvisioningManagedUser() ? DeleteNonRequiredAppsTask.MANAGED_USER 267 : DeleteNonRequiredAppsTask.PROFILE_OWNER), 268 true /* creating new profile */, 269 mManagedProfileOrUserInfo.id, false /* delete non-required system apps */, 270 new DeleteNonRequiredAppsTask.Callback() { 271 272 @Override 273 public void onSuccess() { 274 // Need to explicitly handle exceptions here, as 275 // onError() is not invoked for failures in 276 // onSuccess(). 277 try { 278 disableBluetoothSharingTask.run(); 279 if (!isProvisioningManagedUser()) { 280 managedProfileSettingsTask.run(); 281 disableInstallShortcutListenersTask.run(); 282 } 283 setUpUserOrProfile(); 284 } catch (ProvisioningException e) { 285 error(e.getMessage(), e); 286 } catch (Exception e) { 287 error("Provisioning failed", e); 288 } 289 finish(); 290 } 291 292 @Override 293 public void onError() { 294 // Raise an error with a tracing exception attached. 295 error("Delete non required apps task failed.", new Exception()); 296 finish(); 297 } 298 }); 299 300 deleteNonRequiredAppsTask.run(); 301 } 302 } 303 304 /** 305 * Called when the new profile or managed user is ready for provisioning (the profile is created 306 * and all the apps not needed have been deleted). 307 */ 308 private void setUpUserOrProfile() throws ProvisioningException { 309 installMdmOnManagedProfile(); 310 setMdmAsActiveAdmin(); 311 setMdmAsManagedProfileOwner(); 312 313 if (!isProvisioningManagedUser()) { 314 setOrganizationColor(); 315 setDefaultUserRestrictions(); 316 CrossProfileIntentFiltersHelper.setFilters( 317 getPackageManager(), getUserId(), mManagedProfileOrUserInfo.id); 318 if (!startManagedProfile(mManagedProfileOrUserInfo.id)) { 319 throw raiseError("Could not start user in background"); 320 } 321 // Wait for ACTION_USER_UNLOCKED to be sent before trying to migrate the account. 322 // Even if no account is present, we should not send the provisioning complete broadcast 323 // before the managed profile user is properly started. 324 if ((mUnlockedReceiver != null) && !mUnlockedReceiver.waitForUserUnlocked()) { 325 return; 326 } 327 328 // Note: account migration must happen after setting the profile owner. 329 // Otherwise, there will be a time interval where some apps may think that the account 330 // does not have a profile owner. 331 mUtils.maybeCopyAccount(this, mParams.accountToMigrate, Process.myUserHandle(), 332 mManagedProfileOrUserInfo.getUserHandle()); 333 } 334 } 335 336 /** 337 * Notify the calling activity of our final status, perform any cleanup if 338 * the process didn't succeed. 339 */ 340 private void finish() { 341 ProvisionLogger.logi("Finishing provisioning process, status: " 342 + mProvisioningStatus); 343 // Reached the end of the provisioning process, take appropriate action 344 // based on current mProvisioningStatus. 345 synchronized (this) { 346 switch (mProvisioningStatus) { 347 case STATUS_STARTED: 348 // Provisioning process completed normally. 349 notifyMdmAndCleanup(); 350 mProvisioningStatus = STATUS_DONE; 351 break; 352 case STATUS_UNKNOWN: 353 // No idea how we could end up in finish() in this state, 354 // but for safety treat it as an error and fall-through to 355 // STATUS_ERROR. 356 mLastErrorMessage = "finish() invoked in STATUS_UNKNOWN"; 357 mProvisioningStatus = STATUS_ERROR; 358 break; 359 case STATUS_ERROR: 360 // Process errored out, cleanup partially created managed 361 // profile. 362 cleanupUserProfile(); 363 break; 364 case STATUS_CANCELLING: 365 // User requested cancellation during processing, remove 366 // the successfully created profile. 367 cleanupUserProfile(); 368 mProvisioningStatus = STATUS_CANCELLED; 369 break; 370 case STATUS_CANCELLED: 371 case STATUS_DONE: 372 // Shouldn't be possible to already be in this state?!? 373 ProvisionLogger.logw("finish() invoked multiple times?"); 374 break; 375 } 376 } 377 378 ProvisionLogger.logi("Finished provisioning process, final status: " 379 + mProvisioningStatus); 380 381 // Notify UI activity of final status reached. 382 reportStatus(); 383 } 384 385 /** 386 * Initialize the user that underlies the managed profile. 387 * This is required so that the provisioning complete broadcast can be sent across to the 388 * profile and apps can run on it. 389 */ 390 private boolean startManagedProfile(int userId) { 391 ProvisionLogger.logd("Starting user in background"); 392 IActivityManager iActivityManager = ActivityManagerNative.getDefault(); 393 // Register a receiver for the Intent.ACTION_USER_UNLOCKED to know when the managed profile 394 // has been started and unlocked. 395 mUnlockedReceiver = new UserUnlockedReceiver(this, userId); 396 try { 397 return iActivityManager.startUserInBackground(userId); 398 } catch (RemoteException neverThrown) { 399 // Never thrown, as we are making local calls. 400 ProvisionLogger.loge("This should not happen.", neverThrown); 401 } 402 return false; 403 } 404 405 private void notifyActivityOfSuccess() { 406 Intent successIntent = new Intent(ACTION_PROVISIONING_SUCCESS); 407 LocalBroadcastManager.getInstance(ProfileOwnerProvisioningService.this) 408 .sendBroadcast(successIntent); 409 } 410 411 /** 412 * Notify the mdm that provisioning has completed. When the mdm has received the intent, stop 413 * the service and notify the {@link ProfileOwnerProvisioningActivity} so that it can finish 414 * itself. 415 */ 416 private void notifyMdmAndCleanup() { 417 // Set DPM userProvisioningState appropriately and persist mParams for use during 418 // FinalizationActivity if necessary. 419 mUtils.markUserProvisioningStateInitiallyDone(this, mParams); 420 421 if (mParams.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) { 422 // Set the user_setup_complete flag on the managed-profile as setup-wizard is never run 423 // for that user. This is not relevant for other cases since 424 // Utils.markUserProvisioningStateInitiallyDone() communicates provisioning state to 425 // setup-wizard via DPM.setUserProvisioningState() if necessary. 426 mUtils.markUserSetupComplete(this, mManagedProfileOrUserInfo.id); 427 } 428 429 // If profile owner provisioning was started after current user setup is completed, then we 430 // can directly send the ACTION_PROFILE_PROVISIONING_COMPLETE broadcast to the MDM. 431 // But if the provisioning was started as part of setup wizard flow, we signal setup-wizard 432 // should shutdown via DPM.setUserProvisioningState(), which will result in a finalization 433 // intent being sent to us once setup-wizard finishes. As part of the finalization intent 434 // handling we then broadcast ACTION_PROFILE_PROVISIONING_COMPLETE. 435 if (mUtils.isUserSetupCompleted(this)) { 436 UserHandle managedUserHandle = new UserHandle(mManagedProfileOrUserInfo.id); 437 438 // Use an ordered broadcast, so that we only finish when the mdm has received it. 439 // Avoids a lag in the transition between provisioning and the mdm. 440 BroadcastReceiver mdmReceivedSuccessReceiver = new MdmReceivedSuccessReceiver( 441 mParams.accountToMigrate, mParams.deviceAdminComponentName.getPackageName()); 442 443 Intent completeIntent = new Intent(ACTION_PROFILE_PROVISIONING_COMPLETE); 444 completeIntent.setComponent(mParams.deviceAdminComponentName); 445 completeIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES | 446 Intent.FLAG_RECEIVER_FOREGROUND); 447 if (mParams.adminExtrasBundle != null) { 448 completeIntent.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, 449 mParams.adminExtrasBundle); 450 } 451 452 sendOrderedBroadcastAsUser(completeIntent, managedUserHandle, null, 453 mdmReceivedSuccessReceiver, null, Activity.RESULT_OK, null, null); 454 ProvisionLogger.logd("Provisioning complete broadcast has been sent to user " 455 + managedUserHandle.getIdentifier()); 456 } 457 } 458 459 private void createProfile(String profileName) throws ProvisioningException { 460 461 ProvisionLogger.logd("Creating managed profile with name " + profileName); 462 463 mManagedProfileOrUserInfo = mUserManager.createProfileForUser(profileName, 464 UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_DISABLED, 465 Process.myUserHandle().getIdentifier()); 466 467 if (mManagedProfileOrUserInfo == null) { 468 throw raiseError("Couldn't create profile."); 469 } 470 } 471 472 private void installMdmOnManagedProfile() throws ProvisioningException { 473 ProvisionLogger.logd("Installing mobile device management app " 474 + mParams.deviceAdminComponentName + " on managed profile"); 475 476 try { 477 int status = mIpm.installExistingPackageAsUser( 478 mParams.deviceAdminComponentName.getPackageName(), mManagedProfileOrUserInfo.id); 479 switch (status) { 480 case PackageManager.INSTALL_SUCCEEDED: 481 return; 482 case PackageManager.INSTALL_FAILED_USER_RESTRICTED: 483 // Should not happen because we're not installing a restricted user 484 throw raiseError("Could not install mobile device management app on managed " 485 + "profile because the user is restricted"); 486 case PackageManager.INSTALL_FAILED_INVALID_URI: 487 // Should not happen because we already checked 488 throw raiseError("Could not install mobile device management app on managed " 489 + "profile because the package could not be found"); 490 default: 491 throw raiseError("Could not install mobile device management app on managed " 492 + "profile. Unknown status: " + status); 493 } 494 } catch (RemoteException neverThrown) { 495 // Never thrown, as we are making local calls. 496 ProvisionLogger.loge("This should not happen.", neverThrown); 497 } 498 } 499 500 private void setMdmAsManagedProfileOwner() throws ProvisioningException { 501 ProvisionLogger.logd("Setting package " + mParams.deviceAdminComponentName 502 + " as managed profile owner."); 503 504 DevicePolicyManager dpm = 505 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); 506 if (!dpm.setProfileOwner( 507 mParams.deviceAdminComponentName, 508 mParams.deviceAdminComponentName.getPackageName(), 509 mManagedProfileOrUserInfo.id)) { 510 ProvisionLogger.logw("Could not set profile owner."); 511 throw raiseError("Could not set profile owner."); 512 } 513 } 514 515 private void setMdmAsActiveAdmin() { 516 ProvisionLogger.logd("Setting package " + mParams.deviceAdminComponentName 517 + " as active admin."); 518 519 DevicePolicyManager dpm = 520 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); 521 dpm.setActiveAdmin(mParams.deviceAdminComponentName, true /* refreshing*/, 522 mManagedProfileOrUserInfo.id); 523 } 524 525 private void setOrganizationColor() { 526 if (mParams.mainColor != null) { 527 DevicePolicyManager dpm = 528 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); 529 dpm.setOrganizationColorForUser(mParams.mainColor, mManagedProfileOrUserInfo.id); 530 } 531 } 532 533 private ProvisioningException raiseError(String message) throws ProvisioningException { 534 throw new ProvisioningException(message); 535 } 536 537 /** 538 * Record the fact that an error occurred, change mProvisioningStatus to 539 * reflect the fact the provisioning process failed 540 */ 541 private void error(String dialogMessage, Exception e) { 542 synchronized (this) { 543 // Only case where an error condition should be notified is if we 544 // are in the normal flow for provisioning. If the process has been 545 // cancelled or already completed, then the fact there is an error 546 // is almost irrelevant. 547 if (mProvisioningStatus == STATUS_STARTED) { 548 mProvisioningStatus = STATUS_ERROR; 549 mLastErrorMessage = dialogMessage; 550 551 ProvisionLogger.logw( 552 "Error occured during provisioning process: " 553 + dialogMessage, 554 e); 555 } else { 556 ProvisionLogger.logw( 557 "Unexpected error occured in status [" 558 + mProvisioningStatus + "]: " + dialogMessage, 559 e); 560 } 561 } 562 } 563 564 private void setDefaultUserRestrictions() { 565 mUserManager.setUserRestriction(UserManager.DISALLOW_WALLPAPER, true, 566 mManagedProfileOrUserInfo.getUserHandle()); 567 } 568 569 private void notifyActivityError() { 570 Intent intent = new Intent(ACTION_PROVISIONING_ERROR); 571 intent.putExtra(EXTRA_LOG_MESSAGE_KEY, mLastErrorMessage); 572 LocalBroadcastManager.getInstance(this).sendBroadcast(intent); 573 } 574 575 private void notifyActivityCancelled() { 576 Intent cancelIntent = new Intent(ACTION_PROVISIONING_CANCELLED); 577 LocalBroadcastManager.getInstance(this).sendBroadcast(cancelIntent); 578 } 579 580 /** 581 * Performs cleanup of any created user-profile on failure/cancellation. 582 */ 583 private void cleanupUserProfile() { 584 if (mManagedProfileOrUserInfo != null && !isProvisioningManagedUser()) { 585 ProvisionLogger.logd("Removing managed profile"); 586 mUserManager.removeUser(mManagedProfileOrUserInfo.id); 587 } 588 } 589 590 @Override 591 public IBinder onBind(Intent intent) { 592 return null; 593 } 594 595 /** 596 * Internal exception to allow provisioning process to terminal quickly and 597 * cleanly on first error, rather than continuing to process despite errors 598 * occurring. 599 */ 600 private static class ProvisioningException extends Exception { 601 public ProvisioningException(String detailMessage) { 602 super(detailMessage); 603 } 604 } 605 606 public boolean isProvisioningManagedUser() { 607 return mParams.provisioningAction.equals(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER); 608 } 609 610 /** 611 * BroadcastReceiver that listens to {@link Intent#ACTION_USER_UNLOCKED} in order to provide 612 * a blocking wait until the managed profile has been started and unlocked. 613 */ 614 private static class UserUnlockedReceiver extends BroadcastReceiver { 615 private static final IntentFilter FILTER = new IntentFilter(Intent.ACTION_USER_UNLOCKED); 616 617 private final Semaphore semaphore = new Semaphore(0); 618 private final Context mContext; 619 private final int mUserId; 620 621 UserUnlockedReceiver(Context context, int userId) { 622 mContext = context; 623 mUserId = userId; 624 mContext.registerReceiverAsUser(this, new UserHandle(userId), FILTER, null, null); 625 } 626 627 @Override 628 public void onReceive(Context context, Intent intent ) { 629 if (!Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { 630 ProvisionLogger.logw("Unexpected intent: " + intent); 631 return; 632 } 633 if (intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == mUserId) { 634 ProvisionLogger.logd("Received ACTION_USER_UNLOCKED for user " + mUserId); 635 semaphore.release(); 636 mContext.unregisterReceiver(this); 637 } 638 } 639 640 public boolean waitForUserUnlocked() { 641 ProvisionLogger.logd("Waiting for ACTION_USER_UNLOCKED"); 642 try { 643 return semaphore.tryAcquire(USER_UNLOCKED_TIMEOUT_SECONDS, TimeUnit.SECONDS); 644 } catch (InterruptedException ie) { 645 mContext.unregisterReceiver(this); 646 return false; 647 } 648 } 649 } 650 } 651