• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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