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