ManagedProvisioningActivity.java revision df8cba7fec4b96c113697d1ef67de2e2e196988e
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 17package com.android.managedprovisioning; 18 19import static android.app.admin.DeviceAdminReceiver.ACTION_PROFILE_PROVISIONING_COMPLETE; 20import static android.app.admin.DevicePolicyManager.EXTRA_DEVICE_ADMIN; 21import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME; 22import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME; 23import static com.android.managedprovisioning.UserConsentActivity.USER_CONSENT_KEY; 24 25import android.app.Activity; 26import android.app.ActivityManagerNative; 27import android.app.IActivityManager; 28import android.app.admin.DevicePolicyManager; 29import android.content.ComponentName; 30import android.content.Context; 31import android.content.Intent; 32import android.content.IntentFilter; 33import android.content.pm.IPackageManager; 34import android.content.pm.PackageManager; 35import android.content.pm.PackageManager.NameNotFoundException; 36import android.content.pm.UserInfo; 37import android.os.Bundle; 38import android.os.Process; 39import android.os.RemoteException; 40import android.os.ServiceManager; 41import android.os.UserHandle; 42import android.os.UserManager; 43import android.text.TextUtils; 44import android.view.LayoutInflater; 45import android.view.View; 46 47 48 49import com.android.managedprovisioning.task.DeleteNonRequiredAppsTask; 50 51import java.util.List; 52 53/** 54 * Handles managed profile provisioning: A device that already has a user, but needs to be set up 55 * for a secondary usage purpose (e.g using your personal device as a corporate device). 56 */ 57// TODO: Proper error handling to report back to the user and potentially the mdm. 58public class ManagedProvisioningActivity extends Activity { 59 60 private static final int USER_CONSENT_REQUEST_CODE = 1; 61 private static final int ENCRYPT_DEVICE_REQUEST_CODE = 2; 62 63 private String mMdmPackageName; 64 private ComponentName mActiveAdminComponentName; 65 private String mDefaultManagedProfileName; 66 67 private IPackageManager mIpm; 68 private UserInfo mManagedProfileUserInfo; 69 private UserManager mUserManager; 70 71 private Boolean userConsented; 72 73 @Override 74 protected void onCreate(Bundle savedInstanceState) { 75 super.onCreate(savedInstanceState); 76 77 ProvisionLogger.logd("Managed provisioning activity ONCREATE"); 78 79 PackageManager pm = getPackageManager(); 80 if (!pm.hasSystemFeature(PackageManager.FEATURE_MANAGED_PROFILES)) { 81 showErrorAndClose(R.string.managed_provisioning_not_supported, 82 "Exiting managed provisioning, managed profiles feature is not available"); 83 return; 84 } 85 86 // Initialize member variables from the intent, stop if the intent wasn't valid. 87 try { 88 initialize(getIntent()); 89 } catch (ManagedProvisioningFailedException e) { 90 showErrorAndClose(R.string.managed_provisioning_error_text, e.getMessage()); 91 return; 92 } 93 94 // TODO: update UI 95 final LayoutInflater inflater = getLayoutInflater(); 96 final View contentView = inflater.inflate(R.layout.progress_profile_owner, null); 97 setContentView(contentView); 98 99 mIpm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 100 mUserManager = (UserManager) getSystemService(Context.USER_SERVICE); 101 102 if (!alreadyHasManagedProfile()) { 103 // Ask for user consent. 104 Intent userConsentIntent = new Intent(this, UserConsentActivity.class); 105 startActivityForResult(userConsentIntent, USER_CONSENT_REQUEST_CODE); 106 // Wait for user consent, in onActivityResult 107 } else { 108 showErrorAndClose(R.string.managed_profile_already_present, 109 "The device already has a managed profile, nothing to do."); 110 } 111 } 112 113 private void initialize(Intent intent) 114 throws ManagedProvisioningFailedException { 115 mMdmPackageName = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME); 116 // Validate package name 117 if (TextUtils.isEmpty(mMdmPackageName)) { 118 throw new ManagedProvisioningFailedException("Missing intent extra: " 119 + EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME); 120 } else { 121 // Check if the package is installed 122 try { 123 this.getPackageManager().getPackageInfo(mMdmPackageName, 0); 124 } catch (NameNotFoundException e) { 125 throw new ManagedProvisioningFailedException("Mdm "+ mMdmPackageName 126 + " is not installed. " + e); 127 } 128 } 129 130 mActiveAdminComponentName = intent.getParcelableExtra(EXTRA_DEVICE_ADMIN); 131 if (mActiveAdminComponentName == null) { 132 throw new ManagedProvisioningFailedException("Missing intent extra: " 133 + EXTRA_DEVICE_ADMIN); 134 } 135 136 mDefaultManagedProfileName = getIntent() 137 .getStringExtra(EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME); 138 // Validate profile name 139 if (TextUtils.isEmpty(mDefaultManagedProfileName)) { 140 throw new ManagedProvisioningFailedException("Missing intent extra: " 141 + EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME); 142 } 143 } 144 145 @Override 146 public void onBackPressed() { 147 // TODO: Handle this graciously by stopping the provisioning flow and cleaning up. 148 } 149 150 @Override 151 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 152 153 // Wait for the user to consent before starting managed profile provisioning. 154 if (requestCode == USER_CONSENT_REQUEST_CODE) { 155 if (resultCode == RESULT_OK) { 156 userConsented = data.getBooleanExtra(USER_CONSENT_KEY, false); 157 158 // Only start provisioning if the user has consented. 159 if (!userConsented) { 160 ProvisionLogger.logd("User did not consent to profile creation, " 161 + "cancelling provisioing"); 162 finish(); 163 return; 164 } 165 166 // Ask to encrypt the device before proceeding 167 if (!EncryptDeviceActivity.isDeviceEncrypted()) { 168 Intent encryptIntent = new Intent(this, EncryptDeviceActivity.class) 169 .putExtra(EncryptDeviceActivity.EXTRA_RESUME, getIntent().getExtras()); 170 startActivityForResult(encryptIntent, ENCRYPT_DEVICE_REQUEST_CODE); 171 return; 172 } 173 174 startManagedProfileProvisioning(); 175 } 176 if (resultCode == RESULT_CANCELED) { 177 ProvisionLogger.logd("User consent cancelled."); 178 finish(); 179 } 180 } else if (requestCode == ENCRYPT_DEVICE_REQUEST_CODE) { 181 if (resultCode == RESULT_CANCELED) { 182 // Move back to user consent screen 183 Intent userConsentIntent = new Intent(this, UserConsentActivity.class); 184 startActivityForResult(userConsentIntent, USER_CONSENT_REQUEST_CODE); 185 } 186 } 187 } 188 189 /** 190 * This is the core method of this class. It goes through every provisioning step. 191 */ 192 private void startManagedProfileProvisioning() { 193 194 ProvisionLogger.logd("Starting managed profile provisioning"); 195 // Work through the provisioning steps in their corresponding order 196 197 try { 198 createProfile(mDefaultManagedProfileName); 199 DeleteNonRequiredAppsTask deleteTask = 200 new DeleteNonRequiredAppsTask(this, 201 mMdmPackageName, mManagedProfileUserInfo.id, 202 new DeleteNonRequiredAppsTask.Callback() { 203 204 @Override 205 public void onSuccess() { 206 setUpProfileAndFinish(); 207 } 208 209 @Override 210 public void onError() { 211 cleanup(); 212 showErrorAndClose(R.string.managed_provisioning_error_text, 213 "Delete non required apps task failed."); 214 }}, 215 R.array.required_apps_managed_profile, 216 R.array.vendor_required_apps_managed_profile); 217 deleteTask.run(); 218 } catch (ManagedProvisioningFailedException e) { 219 cleanup(); 220 showErrorAndClose(R.string.managed_provisioning_error_text, 221 "Could not finish managed profile provisioning: " + e.getMessage()); 222 } 223 } 224 225 /** 226 * Called when the new profile is ready for provisioning (the profile is created and all the 227 * apps not needed have been deleted). 228 */ 229 private void setUpProfileAndFinish() { 230 try { 231 installMdmOnManagedProfile(); 232 setMdmAsActiveAdmin(); 233 setMdmAsManagedProfileOwner(); 234 startManagedProfile(); 235 forwardIntentsToPrimaryUser(); 236 sendProvisioningCompleteToManagedProfile(this); 237 ProvisionLogger.logd("Finishing managed profile provisioning."); 238 finish(); 239 } catch (ManagedProvisioningFailedException e) { 240 cleanup(); 241 showErrorAndClose(R.string.managed_provisioning_error_text, 242 "Could not finish managed profile provisioning: " + e.getMessage()); 243 } 244 } 245 246 private void createProfile(String profileName) throws ManagedProvisioningFailedException { 247 248 ProvisionLogger.logd("Creating managed profile with name " + profileName); 249 250 mManagedProfileUserInfo = mUserManager.createProfileForUser(profileName, 251 UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_DISABLED, 252 Process.myUserHandle().getIdentifier()); 253 254 if (mManagedProfileUserInfo == null) { 255 if (UserManager.getMaxSupportedUsers() == mUserManager.getUserCount()) { 256 throw new ManagedProvisioningFailedException( 257 "Profile creation failed, maximum number of users reached."); 258 } else { 259 throw new ManagedProvisioningFailedException( 260 "Couldn't create profile. Reason unknown."); 261 } 262 } 263 } 264 265 /** 266 * Performs cleanup of the device on failure. 267 */ 268 private void cleanup() { 269 // The only cleanup we need to do is remove the profile we created. 270 if (mManagedProfileUserInfo != null) { 271 ProvisionLogger.logd("Removing managed profile"); 272 mUserManager.removeUser(mManagedProfileUserInfo.id); 273 } 274 } 275 276 /** 277 * Initializes the user that underlies the managed profile. 278 * This is required so that the provisioning complete broadcast can be sent across to the 279 * profile and apps can run on it. 280 */ 281 private void startManagedProfile() throws ManagedProvisioningFailedException { 282 ProvisionLogger.logd("Starting user in background"); 283 IActivityManager iActivityManager = ActivityManagerNative.getDefault(); 284 try { 285 boolean success = iActivityManager.startUserInBackground(mManagedProfileUserInfo.id); 286 if (!success) { 287 throw new ManagedProvisioningFailedException("Could not start user in background"); 288 } 289 } catch (RemoteException neverThrown) { 290 // Never thrown, as we are making local calls. 291 ProvisionLogger.loge("This should not happen.", neverThrown); 292 } 293 } 294 295 private void installMdmOnManagedProfile() throws ManagedProvisioningFailedException { 296 297 ProvisionLogger.logd("Installing mobile device management app " + mMdmPackageName + 298 " on managed profile"); 299 300 try { 301 int status = mIpm.installExistingPackageAsUser( 302 mMdmPackageName, mManagedProfileUserInfo.id); 303 switch (status) { 304 case PackageManager.INSTALL_SUCCEEDED: 305 return; 306 case PackageManager.INSTALL_FAILED_USER_RESTRICTED: 307 // Should not happen because we're not installing a restricted user 308 throw new ManagedProvisioningFailedException( 309 "Could not install mobile device management app on managed profile " + 310 "because the user is restricted"); 311 case PackageManager.INSTALL_FAILED_INVALID_URI: 312 // Should not happen because we already checked 313 throw new ManagedProvisioningFailedException( 314 "Could not install mobile device management app on managed profile " + 315 "because the package could not be found"); 316 default: 317 throw new ManagedProvisioningFailedException( 318 "Could not install mobile device management app on managed profile. " + 319 "Unknown status: " + status); 320 } 321 } catch (RemoteException neverThrown) { 322 // Never thrown, as we are making local calls. 323 ProvisionLogger.loge("This should not happen.", neverThrown); 324 } 325 } 326 327 private void setMdmAsManagedProfileOwner() throws ManagedProvisioningFailedException { 328 329 ProvisionLogger.logd("Setting package " + mMdmPackageName + " as managed profile owner."); 330 331 DevicePolicyManager dpm = 332 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); 333 if (!dpm.setProfileOwner( 334 mMdmPackageName, mDefaultManagedProfileName, mManagedProfileUserInfo.id)) { 335 ProvisionLogger.logw("Could not set profile owner."); 336 throw new ManagedProvisioningFailedException("Could not set profile owner."); 337 } 338 } 339 340 private void setMdmAsActiveAdmin() { 341 342 ProvisionLogger.logd("Setting package " + mMdmPackageName + " as active admin."); 343 344 DevicePolicyManager dpm = 345 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); 346 dpm.setActiveAdmin(mActiveAdminComponentName, true /* refreshing*/, 347 mManagedProfileUserInfo.id); 348 } 349 350 public void showErrorAndClose(int resourceId, String logText) { 351 ProvisionLogger.loge(logText); 352 new ManagedProvisioningErrorDialog(getString(resourceId)) 353 .show(getFragmentManager(), "ErrorDialogFragment"); 354 } 355 356 boolean alreadyHasManagedProfile() { 357 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); 358 List<UserInfo> profiles = userManager.getProfiles(getUserId()); 359 for (UserInfo userInfo : profiles) { 360 if (userInfo.isManagedProfile()) return true; 361 } 362 return false; 363 } 364 365 private void sendProvisioningCompleteToManagedProfile(Context context) { 366 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); 367 UserHandle userHandle = userManager.getUserForSerialNumber( 368 mManagedProfileUserInfo.serialNumber); 369 370 Intent completeIntent = new Intent(ACTION_PROFILE_PROVISIONING_COMPLETE); 371 completeIntent.setComponent(mActiveAdminComponentName); 372 completeIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES | 373 Intent.FLAG_RECEIVER_FOREGROUND); 374 context.sendBroadcastAsUser(completeIntent, userHandle); 375 376 ProvisionLogger.logd("Provisioning complete broadcast has been sent to user " 377 + userHandle.getIdentifier()); 378 } 379 380 private void forwardIntentsToPrimaryUser() { 381 ProvisionLogger.logd("Setting forwarding intent filters"); 382 PackageManager pm = getPackageManager(); 383 384 IntentFilter mimeTypeTelephony = new IntentFilter(); 385 mimeTypeTelephony.addAction("android.intent.action.DIAL"); 386 mimeTypeTelephony.addCategory("android.intent.category.DEFAULT"); 387 mimeTypeTelephony.addCategory("android.intent.category.BROWSABLE"); 388 try { 389 mimeTypeTelephony.addDataType("vnd.android.cursor.item/phone"); 390 mimeTypeTelephony.addDataType("vnd.android.cursor.item/person"); 391 mimeTypeTelephony.addDataType("vnd.android.cursor.dir/calls"); 392 } catch (IntentFilter.MalformedMimeTypeException e) { 393 //will not happen 394 } 395 pm.addForwardingIntentFilter(mimeTypeTelephony, false /*non-removable*/, 396 mManagedProfileUserInfo.id, UserHandle.USER_OWNER); 397 398 IntentFilter callDial = new IntentFilter(); 399 callDial.addAction("android.intent.action.DIAL"); 400 callDial.addAction("android.intent.action.CALL"); 401 callDial.addAction("android.intent.action.VIEW"); 402 callDial.addCategory("android.intent.category.DEFAULT"); 403 callDial.addCategory("android.intent.category.BROWSABLE"); 404 callDial.addDataScheme("tel"); 405 callDial.addDataScheme("voicemail"); 406 callDial.addDataScheme("sip"); 407 callDial.addDataScheme("tel"); 408 pm.addForwardingIntentFilter(callDial, false /*non-removable*/, mManagedProfileUserInfo.id, 409 UserHandle.USER_OWNER); 410 411 IntentFilter callDialNoData = new IntentFilter(); 412 callDialNoData.addAction("android.intent.action.DIAL"); 413 callDialNoData.addAction("android.intent.action.CALL"); 414 callDialNoData.addAction("android.intent.action.CALL_BUTTON"); 415 callDialNoData.addCategory("android.intent.category.DEFAULT"); 416 callDialNoData.addCategory("android.intent.category.BROWSABLE"); 417 pm.addForwardingIntentFilter(callDialNoData, false /*non-removable*/, 418 mManagedProfileUserInfo.id, UserHandle.USER_OWNER); 419 420 IntentFilter smsMms = new IntentFilter(); 421 smsMms.addAction("android.intent.action.VIEW"); 422 smsMms.addAction("android.intent.action.SENDTO"); 423 smsMms.addCategory("android.intent.category.DEFAULT"); 424 smsMms.addCategory("android.intent.category.BROWSABLE"); 425 smsMms.addDataScheme("sms"); 426 smsMms.addDataScheme("smsto"); 427 smsMms.addDataScheme("mms"); 428 smsMms.addDataScheme("mmsto"); 429 pm.addForwardingIntentFilter(smsMms, false /*non-removable*/, mManagedProfileUserInfo.id, 430 UserHandle.USER_OWNER); 431 432 IntentFilter setPassword = new IntentFilter(); 433 setPassword.addAction("android.app.action.SET_NEW_PASSWORD"); 434 setPassword.addCategory("android.intent.category.DEFAULT"); 435 pm.addForwardingIntentFilter(setPassword, false /*non-removable*/, 436 mManagedProfileUserInfo.id, UserHandle.USER_OWNER); 437 438 IntentFilter send = new IntentFilter(); 439 send.addAction("android.intent.action.SEND"); 440 send.addAction("android.intent.action.SEND_MULTIPLE"); 441 send.addCategory("android.intent.category.DEFAULT"); 442 try { 443 send.addDataType("*/*"); 444 } catch (IntentFilter.MalformedMimeTypeException e) { 445 //will not happen 446 } 447 pm.addForwardingIntentFilter(send, false /*non-removable*/, 448 UserHandle.USER_OWNER, mManagedProfileUserInfo.id); 449 450 IntentFilter getContent = new IntentFilter(); 451 getContent.addAction("android.intent.action.GET_CONTENT"); 452 getContent.addCategory("android.intent.category.DEFAULT"); 453 getContent.addCategory("android.intent.category.OPENABLE"); 454 try { 455 getContent.addDataType("*/*"); 456 } catch (IntentFilter.MalformedMimeTypeException e) { 457 //will not happen 458 } 459 pm.addForwardingIntentFilter(getContent, false /*non-removable*/, 460 mManagedProfileUserInfo.id, UserHandle.USER_OWNER); 461 462 IntentFilter openDocument = new IntentFilter(); 463 openDocument.addAction("android.intent.action.OPEN_DOCUMENT"); 464 openDocument.addCategory("android.intent.category.DEFAULT"); 465 openDocument.addCategory("android.intent.category.OPENABLE"); 466 try { 467 openDocument.addDataType("*/*"); 468 } catch (IntentFilter.MalformedMimeTypeException e) { 469 //will not happen 470 } 471 pm.addForwardingIntentFilter(openDocument, false /*non-removable*/, 472 mManagedProfileUserInfo.id, UserHandle.USER_OWNER); 473 474 IntentFilter pick = new IntentFilter(); 475 pick.addAction("android.intent.action.PICK"); 476 pick.addCategory("android.intent.category.DEFAULT"); 477 try { 478 pick.addDataType("*/*"); 479 } catch (IntentFilter.MalformedMimeTypeException e) { 480 //will not happen 481 } 482 pm.addForwardingIntentFilter(pick, false /*non-removable*/, 483 mManagedProfileUserInfo.id, UserHandle.USER_OWNER); 484 485 IntentFilter pickNoData = new IntentFilter(); 486 pickNoData.addAction("android.intent.action.PICK"); 487 pickNoData.addCategory("android.intent.category.DEFAULT"); 488 pm.addForwardingIntentFilter(pickNoData, false /*non-removable*/, 489 mManagedProfileUserInfo.id, UserHandle.USER_OWNER); 490 } 491 492 /** 493 * Exception thrown when the managed provisioning has failed completely. 494 * 495 * Note: We're using a custom exception to avoid catching subsequent exceptions that might be 496 * significant. 497 */ 498 private class ManagedProvisioningFailedException extends Exception { 499 public ManagedProvisioningFailedException(String message) { 500 super(message); 501 } 502 } 503} 504 505