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 android.app.Activity; 20 import android.app.AlertDialog; 21 import android.app.admin.DevicePolicyManager; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.content.pm.ApplicationInfo; 27 import android.content.pm.PackageManager; 28 import android.content.pm.ResolveInfo; 29 import android.content.pm.UserInfo; 30 import android.os.Build; 31 import android.os.Bundle; 32 import android.os.PersistableBundle; 33 import android.os.Process; 34 import android.os.SystemProperties; 35 import android.os.UserHandle; 36 import android.os.UserManager; 37 import android.provider.Settings; 38 import android.view.View; 39 import android.widget.ImageView; 40 import android.widget.TextView; 41 42 import com.android.managedprovisioning.DeleteManagedProfileDialog.DeleteManagedProfileCallback; 43 import com.android.managedprovisioning.UserConsentDialog.ConsentCallback; 44 import com.android.managedprovisioning.Utils.IllegalProvisioningArgumentException; 45 import com.android.managedprovisioning.Utils.MdmPackageInfo; 46 47 import java.util.List; 48 49 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE; 50 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME; 51 import static com.android.managedprovisioning.EncryptDeviceActivity.EXTRA_RESUME; 52 import static com.android.managedprovisioning.EncryptDeviceActivity.EXTRA_RESUME_TARGET; 53 import static com.android.managedprovisioning.EncryptDeviceActivity.TARGET_PROFILE_OWNER; 54 55 /** 56 * The activity sets up the environment in which the {@link ProfileOwnerProvisioningActivity} can be run. 57 * It makes sure the device is encrypted, the current launcher supports managed profiles, the 58 * provisioning intent extras are valid, and that the already present managed profile is removed. 59 */ 60 public class ProfileOwnerPreProvisioningActivity extends SetupLayoutActivity 61 implements ConsentCallback, DeleteManagedProfileCallback { 62 63 private static final String MANAGE_USERS_PERMISSION = "android.permission.MANAGE_USERS"; 64 65 // Note: must match the constant defined in HomeSettings 66 private static final String EXTRA_SUPPORT_MANAGED_PROFILES = "support_managed_profiles"; 67 68 // Aliases to start profile owner provisioning with and without MANAGE_USERS permission 69 protected static final ComponentName ALIAS_CHECK_CALLER = 70 new ComponentName("com.android.managedprovisioning", 71 "com.android.managedprovisioning.ProfileOwnerProvisioningActivity"); 72 73 protected static final ComponentName ALIAS_NO_CHECK_CALLER = 74 new ComponentName("com.android.managedprovisioning", 75 "com.android.managedprovisioning.ProfileOwnerProvisioningActivityNoCallerCheck"); 76 77 protected static final int PROVISIONING_REQUEST_CODE = 3; 78 protected static final int ENCRYPT_DEVICE_REQUEST_CODE = 2; 79 protected static final int CHANGE_LAUNCHER_REQUEST_CODE = 1; 80 81 private DeleteManagedProfileDialog mDeleteDialog; 82 83 private ProvisioningParams mParams; 84 private final MessageParser mParser = new MessageParser(); 85 86 @Override onCreate(Bundle savedInstanceState)87 protected void onCreate(Bundle savedInstanceState) { 88 super.onCreate(savedInstanceState); 89 90 initializeLayoutParams(R.layout.user_consent, R.string.setup_work_profile, false); 91 configureNavigationButtons(R.string.set_up, View.INVISIBLE, View.VISIBLE); 92 93 TextView consentMessageTextView = (TextView) findViewById(R.id.user_consent_message); 94 consentMessageTextView.setText(R.string.company_controls_workspace); 95 TextView mdmInfoTextView = (TextView) findViewById(R.id.mdm_info_message); 96 mdmInfoTextView.setText(R.string.the_following_is_your_mdm); 97 98 // Check whether system has the required managed profile feature. 99 if (!systemHasManagedProfileFeature()) { 100 showErrorAndClose(R.string.managed_provisioning_not_supported, 101 "Exiting managed profile provisioning, " 102 + "managed profiles feature is not available"); 103 return; 104 } 105 if (Process.myUserHandle().getIdentifier() != UserHandle.USER_OWNER) { 106 showErrorAndClose(R.string.user_is_not_owner, 107 "Exiting managed profile provisioning, calling user is not owner."); 108 return; 109 } 110 if (Utils.hasDeviceOwner(this)) { 111 showErrorAndClose(R.string.device_owner_exists, 112 "Exiting managed profile provisioning, a device owner exists"); 113 } 114 115 // Initialize member variables from the intent, stop if the intent wasn't valid. 116 try { 117 initialize(getIntent(), getPackageName().equals(getCallingPackage())); 118 } catch (IllegalProvisioningArgumentException e) { 119 showErrorAndClose(R.string.managed_provisioning_error_text, e.getMessage()); 120 return; 121 } 122 123 setMdmIcon(mParams.deviceAdminPackageName); 124 125 // If the caller started us via ALIAS_NO_CHECK_CALLER then they must have permission to 126 // MANAGE_USERS since it is a restricted intent. Otherwise, check the calling package. 127 boolean hasManageUsersPermission = (getComponentName().equals(ALIAS_NO_CHECK_CALLER)); 128 if (!hasManageUsersPermission) { 129 // Calling package has to equal the requested device admin package or has to be system. 130 String callingPackage = getCallingPackage(); 131 if (callingPackage == null) { 132 showErrorAndClose(R.string.managed_provisioning_error_text, 133 "Calling package is null. " + 134 "Was startActivityForResult used to start this activity?"); 135 return; 136 } 137 if (!callingPackage.equals(mParams.deviceAdminPackageName) 138 && !packageHasManageUsersPermission(callingPackage)) { 139 showErrorAndClose(R.string.managed_provisioning_error_text, "Permission denied, " 140 + "calling package tried to set a different package as profile owner. " 141 + "The system MANAGE_USERS permission is required."); 142 return; 143 } 144 } 145 146 // If there is already a managed profile, setup the profile deletion dialog. 147 // Otherwise, check whether system has reached maximum user limit. 148 int existingManagedProfileUserId = Utils.alreadyHasManagedProfile(this); 149 if (existingManagedProfileUserId != -1) { 150 DevicePolicyManager dpm = 151 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); 152 createDeleteManagedProfileDialog(dpm, existingManagedProfileUserId); 153 } else if (isMaximumManagedProfilesLimitReached()) { 154 showErrorAndClose(R.string.maximum_user_limit_reached, 155 "Exiting managed profile provisioning, cannot add more users."); 156 } else { 157 showStartProvisioningButton(); 158 } 159 } 160 161 @Override onResume()162 protected void onResume() { 163 super.onResume(); 164 165 setTitle(R.string.setup_profile_start_setup); 166 if (Utils.alreadyHasManagedProfile(this) != -1) { 167 showDeleteManagedProfileDialog(); 168 } 169 } 170 171 @Override onPause()172 protected void onPause() { 173 super.onPause(); 174 175 hideDeleteManagedProfileDialog(); 176 } 177 showStartProvisioningButton()178 private void showStartProvisioningButton() { 179 mNextButton.setVisibility(View.VISIBLE); 180 } 181 packageHasManageUsersPermission(String pkg)182 private boolean packageHasManageUsersPermission(String pkg) { 183 return PackageManager.PERMISSION_GRANTED == getPackageManager() 184 .checkPermission(MANAGE_USERS_PERMISSION, pkg); 185 } 186 systemHasManagedProfileFeature()187 private boolean systemHasManagedProfileFeature() { 188 PackageManager pm = getPackageManager(); 189 return pm.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS); 190 } 191 isMaximumManagedProfilesLimitReached()192 private boolean isMaximumManagedProfilesLimitReached() { 193 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); 194 return !userManager.canAddMoreManagedProfiles(); 195 } 196 currentLauncherSupportsManagedProfiles()197 private boolean currentLauncherSupportsManagedProfiles() { 198 Intent intent = new Intent(Intent.ACTION_MAIN); 199 intent.addCategory(Intent.CATEGORY_HOME); 200 201 PackageManager pm = getPackageManager(); 202 ResolveInfo launcherResolveInfo 203 = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); 204 if (launcherResolveInfo == null) { 205 return false; 206 } 207 try { 208 // If the user has not chosen a default launcher, then launcherResolveInfo will be 209 // referring to the resolver activity. It is fine to create a managed profile in 210 // this case since there will always be at least one launcher on the device that 211 // supports managed profile feature. 212 ApplicationInfo launcherAppInfo = getPackageManager().getApplicationInfo( 213 launcherResolveInfo.activityInfo.packageName, 0 /* default flags */); 214 return versionNumberAtLeastL(launcherAppInfo.targetSdkVersion); 215 } catch (PackageManager.NameNotFoundException e) { 216 return false; 217 } 218 } 219 versionNumberAtLeastL(int versionNumber)220 private boolean versionNumberAtLeastL(int versionNumber) { 221 return versionNumber >= Build.VERSION_CODES.LOLLIPOP; 222 } 223 setMdmIcon(String packageName)224 private void setMdmIcon(String packageName) { 225 MdmPackageInfo packageInfo = Utils.getMdmPackageInfo(getPackageManager(), packageName); 226 if (packageInfo != null) { 227 String appLabel = packageInfo.getAppLabel(); 228 ImageView imageView = (ImageView) findViewById(R.id.mdm_icon_view); 229 imageView.setImageDrawable(packageInfo.getPackageIcon()); 230 imageView.setContentDescription( 231 getResources().getString(R.string.mdm_icon_label, appLabel)); 232 233 TextView deviceManagerName = (TextView) findViewById(R.id.device_manager_name); 234 deviceManagerName.setText(appLabel); 235 } 236 } 237 238 /** 239 * Checks if all required provisioning parameters are provided. 240 * Does not check for extras that are optional such as wifi ssid. 241 * Also checks whether type of admin extras bundle (if present) is PersistableBundle. 242 * 243 * @param intent The intent that started provisioning 244 * @param trusted Whether the intent is trusted or not. 245 */ initialize(Intent intent, boolean trusted)246 private void initialize(Intent intent, boolean trusted) 247 throws IllegalProvisioningArgumentException { 248 mParams = mParser.parseNonNfcIntent(intent, trusted); 249 250 mParams.deviceAdminComponentName = Utils.findDeviceAdmin( 251 mParams.deviceAdminPackageName, mParams.deviceAdminComponentName, this); 252 mParams.deviceAdminPackageName = mParams.deviceAdminComponentName.getPackageName(); 253 254 } 255 256 /** 257 * If the device is encrypted start the service which does the provisioning, otherwise ask for 258 * user consent to encrypt the device. 259 */ checkEncryptedAndStartProvisioningService()260 private void checkEncryptedAndStartProvisioningService() { 261 if (EncryptDeviceActivity.isDeviceEncrypted() 262 || SystemProperties.getBoolean("persist.sys.no_req_encrypt", false)) { 263 264 // Notify the user once more that the admin will have full control over the profile, 265 // then start provisioning. 266 UserConsentDialog.newInstance(UserConsentDialog.PROFILE_OWNER) 267 .show(getFragmentManager(), "UserConsentDialogFragment"); 268 } else { 269 Bundle resumeExtras = new Bundle(); 270 resumeExtras.putString(EXTRA_RESUME_TARGET, TARGET_PROFILE_OWNER); 271 mParser.addProvisioningParamsToBundle(resumeExtras, mParams); 272 Intent encryptIntent = new Intent(this, EncryptDeviceActivity.class) 273 .putExtra(EXTRA_RESUME, resumeExtras); 274 startActivityForResult(encryptIntent, ENCRYPT_DEVICE_REQUEST_CODE); 275 // Continue in onActivityResult or after reboot. 276 } 277 } 278 279 @Override onDialogConsent()280 public void onDialogConsent() { 281 // For accessibility purposes: we need to talk back only the title of the 282 // next screen after user clicks ok. 283 setTitle(""); 284 setupEnvironmentAndProvision(); 285 } 286 287 @Override onDialogCancel()288 public void onDialogCancel() { 289 // Do nothing. 290 } 291 292 @Override onRemoveProfileApproval(int existingManagedProfileUserId)293 public void onRemoveProfileApproval(int existingManagedProfileUserId) { 294 mDeleteDialog = null; 295 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); 296 userManager.removeUser(existingManagedProfileUserId); 297 showStartProvisioningButton(); 298 } 299 300 @Override onRemoveProfileCancel()301 public void onRemoveProfileCancel() { 302 setResult(Activity.RESULT_CANCELED); 303 finish(); 304 } 305 setupEnvironmentAndProvision()306 private void setupEnvironmentAndProvision() { 307 // Remove any pre-provisioning UI in favour of progress display 308 BootReminder.cancelProvisioningReminder(this); 309 310 // Check whether the current launcher supports managed profiles. 311 if (!currentLauncherSupportsManagedProfiles()) { 312 showCurrentLauncherInvalid(); 313 } else { 314 startProfileOwnerProvisioning(); 315 } 316 } 317 pickLauncher()318 private void pickLauncher() { 319 Intent changeLauncherIntent = new Intent("android.settings.HOME_SETTINGS"); 320 changeLauncherIntent.putExtra(EXTRA_SUPPORT_MANAGED_PROFILES, true); 321 startActivityForResult(changeLauncherIntent, CHANGE_LAUNCHER_REQUEST_CODE); 322 // Continue in onActivityResult. 323 } 324 startProfileOwnerProvisioning()325 private void startProfileOwnerProvisioning() { 326 Intent intent = new Intent(this, ProfileOwnerProvisioningActivity.class); 327 intent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, mParams); 328 startActivityForResult(intent, PROVISIONING_REQUEST_CODE); 329 // Set cross-fade transition animation into the interstitial progress activity. 330 overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); 331 } 332 333 @Override onActivityResult(int requestCode, int resultCode, Intent data)334 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 335 if (requestCode == ENCRYPT_DEVICE_REQUEST_CODE) { 336 if (resultCode == RESULT_CANCELED) { 337 ProvisionLogger.loge("User canceled device encryption."); 338 setResult(Activity.RESULT_CANCELED); 339 finish(); 340 } 341 } else if (requestCode == CHANGE_LAUNCHER_REQUEST_CODE) { 342 if (resultCode == RESULT_CANCELED) { 343 showCurrentLauncherInvalid(); 344 } else if (resultCode == RESULT_OK) { 345 startProfileOwnerProvisioning(); 346 } 347 } 348 if (requestCode == PROVISIONING_REQUEST_CODE) { 349 setResult(resultCode); 350 finish(); 351 } 352 } 353 showCurrentLauncherInvalid()354 private void showCurrentLauncherInvalid() { 355 new AlertDialog.Builder(this) 356 .setCancelable(false) 357 .setMessage(R.string.managed_provisioning_not_supported_by_launcher) 358 .setNegativeButton(R.string.cancel_provisioning, 359 new DialogInterface.OnClickListener() { 360 @Override 361 public void onClick(DialogInterface dialog,int id) { 362 dialog.dismiss(); 363 setResult(Activity.RESULT_CANCELED); 364 finish(); 365 } 366 }) 367 .setPositiveButton(R.string.pick_launcher, 368 new DialogInterface.OnClickListener() { 369 @Override 370 public void onClick(DialogInterface dialog,int id) { 371 pickLauncher(); 372 } 373 }) 374 .show(); 375 } 376 showErrorAndClose(int resourceId, String logText)377 public void showErrorAndClose(int resourceId, String logText) { 378 ProvisionLogger.loge(logText); 379 new AlertDialog.Builder(this) 380 .setTitle(R.string.provisioning_error_title) 381 .setMessage(resourceId) 382 .setCancelable(false) 383 .setPositiveButton(R.string.device_owner_error_ok, 384 new DialogInterface.OnClickListener() { 385 @Override 386 public void onClick(DialogInterface dialog,int id) { 387 // Close activity 388 ProfileOwnerPreProvisioningActivity.this.setResult( 389 Activity.RESULT_CANCELED); 390 ProfileOwnerPreProvisioningActivity.this.finish(); 391 } 392 }) 393 .show(); 394 } 395 396 /** 397 * Builds a dialog that allows the user to remove an existing managed profile. 398 */ createDeleteManagedProfileDialog(DevicePolicyManager dpm, int existingManagedProfileUserId)399 private void createDeleteManagedProfileDialog(DevicePolicyManager dpm, 400 int existingManagedProfileUserId) { 401 if (mDeleteDialog != null) { 402 return; 403 } 404 405 ComponentName mdmPackageName = dpm.getProfileOwnerAsUser(existingManagedProfileUserId); 406 String domainName = dpm.getProfileOwnerNameAsUser(existingManagedProfileUserId); 407 408 mDeleteDialog = DeleteManagedProfileDialog.newInstance(existingManagedProfileUserId, 409 mdmPackageName, domainName); 410 } 411 showDeleteManagedProfileDialog()412 private void showDeleteManagedProfileDialog() { 413 if (mDeleteDialog == null) { 414 return; 415 } 416 417 if (!mDeleteDialog.isVisible()) { 418 mDeleteDialog.show(getFragmentManager(), "DeleteManagedProfileDialogFragment"); 419 } 420 } 421 hideDeleteManagedProfileDialog()422 private void hideDeleteManagedProfileDialog() { 423 if (mDeleteDialog == null) { 424 return; 425 } 426 427 mDeleteDialog.dismiss(); 428 mDeleteDialog = null; 429 } 430 431 @Override onBackPressed()432 public void onBackPressed() { 433 new AlertDialog.Builder(this) 434 .setTitle(R.string.work_profile_setup_later_title) 435 .setMessage(R.string.work_profile_setup_later_message) 436 .setCancelable(false) 437 .setPositiveButton(R.string.work_profile_setup_stop, 438 new DialogInterface.OnClickListener() { 439 @Override 440 public void onClick(DialogInterface dialog,int id) { 441 ProfileOwnerPreProvisioningActivity.this.setResult( 442 Activity.RESULT_CANCELED); 443 ProfileOwnerPreProvisioningActivity.this.finish(); 444 } 445 }) 446 .setNegativeButton(R.string.work_profile_setup_continue, 447 new DialogInterface.OnClickListener() { 448 @Override 449 public void onClick(DialogInterface dialog, int id) { 450 // user chose to continue. Do nothing 451 } 452 }) 453 .show(); 454 } 455 456 @Override onNavigateNext()457 public void onNavigateNext() { 458 checkEncryptedAndStartProvisioningService(); 459 } 460 } 461