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