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