1 /* 2 * Copyright 2016, 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.preprovisioning; 18 19 import static android.app.admin.DevicePolicyManager.ACTION_ADMIN_POLICY_COMPLIANCE; 20 import static android.app.admin.DevicePolicyManager.ACTION_GET_PROVISIONING_MODE; 21 22 import static com.android.managedprovisioning.model.ProvisioningParams.PROVISIONING_MODE_FINANCED_DEVICE; 23 import static com.android.managedprovisioning.model.ProvisioningParams.PROVISIONING_MODE_FULLY_MANAGED_DEVICE_LEGACY; 24 25 import android.annotation.IntDef; 26 import android.annotation.Nullable; 27 import android.app.Activity; 28 import android.app.ActivityManager; 29 import android.app.DialogFragment; 30 import android.content.ComponentName; 31 import android.content.Intent; 32 import android.os.Bundle; 33 import android.os.UserHandle; 34 import android.provider.Settings; 35 import android.view.ContextMenu; 36 import android.view.ContextMenu.ContextMenuInfo; 37 import android.view.View; 38 import android.widget.TextView; 39 40 import androidx.annotation.VisibleForTesting; 41 42 import com.android.managedprovisioning.R; 43 import com.android.managedprovisioning.common.AccessibilityContextMenuMaker; 44 import com.android.managedprovisioning.common.LogoUtils; 45 import com.android.managedprovisioning.common.ProvisionLogger; 46 import com.android.managedprovisioning.common.SetupGlifLayoutActivity; 47 import com.android.managedprovisioning.common.SimpleDialog; 48 import com.android.managedprovisioning.common.Utils; 49 import com.android.managedprovisioning.model.CustomizationParams; 50 import com.android.managedprovisioning.model.ProvisioningParams; 51 import com.android.managedprovisioning.preprovisioning.PreProvisioningController.UiParams; 52 import com.android.managedprovisioning.preprovisioning.consent.ConsentUiHelperFactory; 53 import com.android.managedprovisioning.preprovisioning.consent.ConsentUiHelper; 54 import com.android.managedprovisioning.preprovisioning.consent.ConsentUiHelperCallback; 55 import com.android.managedprovisioning.provisioning.FinancedDeviceLandingActivity; 56 import com.android.managedprovisioning.provisioning.LandingActivity; 57 import com.android.managedprovisioning.provisioning.ProvisioningActivity; 58 import com.google.android.setupcompat.util.WizardManagerHelper; 59 60 import java.lang.annotation.Retention; 61 import java.lang.annotation.RetentionPolicy; 62 63 public class PreProvisioningActivity extends SetupGlifLayoutActivity implements 64 SimpleDialog.SimpleDialogListener, PreProvisioningController.Ui, ConsentUiHelperCallback { 65 66 private static final String KEY_ACTIVITY_STATE = "activity-state"; 67 68 private static final int ENCRYPT_DEVICE_REQUEST_CODE = 1; 69 @VisibleForTesting 70 protected static final int PROVISIONING_REQUEST_CODE = 2; 71 private static final int WIFI_REQUEST_CODE = 3; 72 private static final int CHANGE_LAUNCHER_REQUEST_CODE = 4; 73 private static final int ADMIN_INTEGRATED_FLOW_PREPARE_REQUEST_CODE = 5; 74 private static final int GET_PROVISIONING_MODE_REQUEST_CODE = 6; 75 private static final int FINANCED_DEVICE_PREPARE_REQUEST_CODE = 7; 76 77 // Note: must match the constant defined in HomeSettings 78 private static final String EXTRA_SUPPORT_MANAGED_PROFILES = "support_managed_profiles"; 79 private static final String SAVED_PROVISIONING_PARAMS = "saved_provisioning_params"; 80 81 private static final String ERROR_AND_CLOSE_DIALOG = "PreProvErrorAndCloseDialog"; 82 private static final String BACK_PRESSED_DIALOG_RESET = "PreProvBackPressedDialogReset"; 83 private static final String BACK_PRESSED_DIALOG_CLOSE_ACTIVITY = 84 "PreProvBackPressedDialogCloseActivity"; 85 private static final String LAUNCHER_INVALID_DIALOG = "PreProvCurrentLauncherInvalidDialog"; 86 private static final String DELETE_MANAGED_PROFILE_DIALOG = "PreProvDeleteManagedProfileDialog"; 87 88 private PreProvisioningController mController; 89 private ControllerProvider mControllerProvider; 90 private final AccessibilityContextMenuMaker mContextMenuMaker; 91 private ConsentUiHelper mConsentUiHelper; 92 93 static final int STATE_PREPROVISIONING_INTIIALIZING = 1; 94 static final int STATE_PROVISIONING_STARTED = 2; 95 static final int STATE_PROVISIONING_FINALIZED = 3; 96 97 @Retention(RetentionPolicy.SOURCE) 98 @IntDef({STATE_PREPROVISIONING_INTIIALIZING, 99 STATE_PROVISIONING_STARTED, 100 STATE_PROVISIONING_FINALIZED}) 101 private @interface PreProvisioningState {} 102 103 private @PreProvisioningState int mState; 104 105 private static final String ERROR_DIALOG_RESET = "ErrorDialogReset"; 106 PreProvisioningActivity()107 public PreProvisioningActivity() { 108 this(activity -> new PreProvisioningController(activity, activity), null, new Utils()); 109 } 110 111 @VisibleForTesting PreProvisioningActivity(ControllerProvider controllerProvider, AccessibilityContextMenuMaker contextMenuMaker, Utils utils)112 public PreProvisioningActivity(ControllerProvider controllerProvider, 113 AccessibilityContextMenuMaker contextMenuMaker, Utils utils) { 114 super(utils); 115 mControllerProvider = controllerProvider; 116 mContextMenuMaker = 117 contextMenuMaker != null ? contextMenuMaker : new AccessibilityContextMenuMaker( 118 this); 119 } 120 121 @Override onCreate(Bundle savedInstanceState)122 protected void onCreate(Bundle savedInstanceState) { 123 super.onCreate(savedInstanceState); 124 125 mState = savedInstanceState == null 126 ? STATE_PREPROVISIONING_INTIIALIZING 127 : savedInstanceState.getInt(KEY_ACTIVITY_STATE, STATE_PREPROVISIONING_INTIIALIZING); 128 129 mController = mControllerProvider.getInstance(this); 130 mConsentUiHelper = ConsentUiHelperFactory.getInstance( 131 /* activity */ this, /* contextMenuMaker */ mContextMenuMaker, 132 /* callback */ this, /* utils */ mUtils, mController.getSettingsFacade()); 133 if (mState == STATE_PREPROVISIONING_INTIIALIZING) { 134 ProvisioningParams params = savedInstanceState == null ? null 135 : savedInstanceState.getParcelable(SAVED_PROVISIONING_PARAMS); 136 mController.initiateProvisioning(getIntent(), params, getCallingPackage()); 137 } 138 } 139 140 @Override finish()141 public void finish() { 142 // The user has backed out of provisioning, so we perform the necessary clean up steps. 143 LogoUtils.cleanUp(this); 144 ProvisioningParams params = mController.getParams(); 145 if (params != null) { 146 params.cleanUp(); 147 } 148 EncryptionController.getInstance(this).cancelEncryptionReminder(); 149 super.finish(); 150 } 151 152 @Override onSaveInstanceState(Bundle outState)153 protected void onSaveInstanceState(Bundle outState) { 154 super.onSaveInstanceState(outState); 155 outState.putParcelable(SAVED_PROVISIONING_PARAMS, mController.getParams()); 156 outState.putInt(KEY_ACTIVITY_STATE, mState); 157 } 158 159 @Override onActivityResult(int requestCode, int resultCode, Intent data)160 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 161 switch (requestCode) { 162 case ENCRYPT_DEVICE_REQUEST_CODE: 163 if (resultCode == RESULT_CANCELED) { 164 ProvisionLogger.loge("User canceled device encryption."); 165 } 166 break; 167 case PROVISIONING_REQUEST_CODE: 168 mState = STATE_PROVISIONING_FINALIZED; 169 setResult(resultCode); 170 finish(); 171 break; 172 case CHANGE_LAUNCHER_REQUEST_CODE: 173 mController.continueProvisioningAfterUserConsent(); 174 break; 175 case WIFI_REQUEST_CODE: 176 if (resultCode == RESULT_CANCELED) { 177 ProvisionLogger.loge("User canceled wifi picking."); 178 setResult(resultCode); 179 finish(); 180 } else { 181 if (resultCode == RESULT_OK) { 182 ProvisionLogger.logd("Wifi request result is OK"); 183 } 184 mController.initiateProvisioning(getIntent(), null /* cached params */, 185 getCallingPackage()); 186 } 187 break; 188 case ADMIN_INTEGRATED_FLOW_PREPARE_REQUEST_CODE: 189 if (resultCode == RESULT_OK) { 190 maybeShowAdminGetProvisioningModeScreen(); 191 } else { 192 setResult(resultCode); 193 finish(); 194 } 195 break; 196 case GET_PROVISIONING_MODE_REQUEST_CODE: 197 if (resultCode == RESULT_OK) { 198 if(data != null && mController.updateProvisioningParamsFromIntent(data)) { 199 mController.showUserConsentScreen(); 200 } else { 201 ProvisionLogger.loge( 202 "Invalid data object returned from GET_PROVISIONING_MODE."); 203 showFactoryResetDialog(R.string.cant_set_up_device, 204 R.string.contact_your_admin_for_help); 205 } 206 } else { 207 ProvisionLogger.loge("Invalid result code from GET_PROVISIONING_MODE. Expected " 208 + RESULT_OK + " but got " + resultCode + "."); 209 showFactoryResetDialog(R.string.cant_set_up_device, 210 R.string.contact_your_admin_for_help); 211 } 212 break; 213 case FINANCED_DEVICE_PREPARE_REQUEST_CODE: 214 if (resultCode == RESULT_OK) { 215 startFinancedDeviceFlow(); 216 } else { 217 setResult(resultCode); 218 finish(); 219 } 220 break; 221 default: 222 ProvisionLogger.logw("Unknown result code :" + resultCode); 223 break; 224 } 225 } 226 227 @Override showErrorAndClose(Integer titleId, int messageId, String logText)228 public void showErrorAndClose(Integer titleId, int messageId, String logText) { 229 ProvisionLogger.loge(logText); 230 231 SimpleDialog.Builder dialogBuilder = new SimpleDialog.Builder() 232 .setTitle(titleId) 233 .setMessage(messageId) 234 .setCancelable(false) 235 .setPositiveButtonMessage(R.string.device_owner_error_ok); 236 showDialog(dialogBuilder, ERROR_AND_CLOSE_DIALOG); 237 } 238 239 @Override onNegativeButtonClick(DialogFragment dialog)240 public void onNegativeButtonClick(DialogFragment dialog) { 241 switch (dialog.getTag()) { 242 case BACK_PRESSED_DIALOG_CLOSE_ACTIVITY: 243 case BACK_PRESSED_DIALOG_RESET: 244 // user chose to continue. Do nothing 245 break; 246 case LAUNCHER_INVALID_DIALOG: 247 dialog.dismiss(); 248 break; 249 case DELETE_MANAGED_PROFILE_DIALOG: 250 setResult(Activity.RESULT_CANCELED); 251 finish(); 252 break; 253 default: 254 SimpleDialog.throwButtonClickHandlerNotImplemented(dialog); 255 } 256 } 257 258 @Override onPositiveButtonClick(DialogFragment dialog)259 public void onPositiveButtonClick(DialogFragment dialog) { 260 switch (dialog.getTag()) { 261 case ERROR_AND_CLOSE_DIALOG: 262 case BACK_PRESSED_DIALOG_CLOSE_ACTIVITY: 263 onProvisioningAborted(); 264 break; 265 case BACK_PRESSED_DIALOG_RESET: 266 mUtils.sendFactoryResetBroadcast(this, 267 "Provisioning cancelled by user on consent screen"); 268 onProvisioningAborted(); 269 break; 270 case LAUNCHER_INVALID_DIALOG: 271 requestLauncherPick(); 272 break; 273 case DELETE_MANAGED_PROFILE_DIALOG: 274 DeleteManagedProfileDialog d = (DeleteManagedProfileDialog) dialog; 275 mController.removeUser(d.getUserId()); 276 mController.initiateProvisioning(getIntent(), /* cached params */ null, 277 getCallingPackage()); 278 break; 279 case ERROR_DIALOG_RESET: 280 getUtils().sendFactoryResetBroadcast(this, "Error during preprovisioning"); 281 setResult(Activity.RESULT_CANCELED); 282 finish(); 283 break; 284 default: 285 SimpleDialog.throwButtonClickHandlerNotImplemented(dialog); 286 } 287 } 288 onProvisioningAborted()289 private void onProvisioningAborted() { 290 setResult(Activity.RESULT_CANCELED); 291 mController.logPreProvisioningCancelled(); 292 finish(); 293 } 294 295 @Override requestEncryption(ProvisioningParams params)296 public void requestEncryption(ProvisioningParams params) { 297 Intent encryptIntent = new Intent(this, EncryptDeviceActivity.class); 298 WizardManagerHelper.copyWizardManagerExtras(getIntent(), encryptIntent); 299 encryptIntent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params); 300 startActivityForResult(encryptIntent, ENCRYPT_DEVICE_REQUEST_CODE); 301 } 302 303 @Override requestWifiPick()304 public void requestWifiPick() { 305 final Intent intent = mUtils.getWifiPickIntent(); 306 WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent); 307 startActivityForResult(intent, WIFI_REQUEST_CODE); 308 } 309 310 @Override showCurrentLauncherInvalid()311 public void showCurrentLauncherInvalid() { 312 SimpleDialog.Builder dialogBuilder = new SimpleDialog.Builder() 313 .setCancelable(false) 314 .setTitle(R.string.change_device_launcher) 315 .setMessage(R.string.launcher_app_cant_be_used_by_work_profile) 316 .setNegativeButtonMessage(R.string.cancel_provisioning) 317 .setPositiveButtonMessage(R.string.pick_launcher); 318 showDialog(dialogBuilder, LAUNCHER_INVALID_DIALOG); 319 } 320 321 @Override abortProvisioning()322 public void abortProvisioning() { 323 onProvisioningAborted(); 324 } 325 requestLauncherPick()326 private void requestLauncherPick() { 327 Intent changeLauncherIntent = new Intent(Settings.ACTION_HOME_SETTINGS); 328 changeLauncherIntent.putExtra(EXTRA_SUPPORT_MANAGED_PROFILES, true); 329 startActivityForResult(changeLauncherIntent, CHANGE_LAUNCHER_REQUEST_CODE); 330 } 331 startProvisioning(int userId, ProvisioningParams params)332 public void startProvisioning(int userId, ProvisioningParams params) { 333 mState = STATE_PROVISIONING_STARTED; 334 Intent intent = new Intent(this, ProvisioningActivity.class); 335 WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent); 336 intent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params); 337 startActivityForResultAsUser(intent, PROVISIONING_REQUEST_CODE, new UserHandle(userId)); 338 overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); 339 } 340 maybeShowAdminGetProvisioningModeScreen()341 private void maybeShowAdminGetProvisioningModeScreen() { 342 final String adminPackage = mController.getParams().inferDeviceAdminPackageName(); 343 final Intent intentGetMode = new Intent(ACTION_GET_PROVISIONING_MODE); 344 intentGetMode.setPackage(adminPackage); 345 final Intent intentPolicy = new Intent(ACTION_ADMIN_POLICY_COMPLIANCE); 346 intentPolicy.setPackage(adminPackage); 347 final ActivityManager activityManager = getSystemService(ActivityManager.class); 348 if (!activityManager.isLowRamDevice() 349 && !mController.getParams().isNfc 350 && intentGetMode.resolveActivity(getPackageManager()) != null 351 && intentPolicy.resolveActivity(getPackageManager()) != null) { 352 mController.putExtrasIntoGetModeIntent(intentGetMode); 353 startActivityForResult(intentGetMode, GET_PROVISIONING_MODE_REQUEST_CODE); 354 } else { 355 startManagedDeviceLegacyFlow(); 356 } 357 } 358 startManagedDeviceLegacyFlow()359 private void startManagedDeviceLegacyFlow() { 360 mController.setProvisioningMode(PROVISIONING_MODE_FULLY_MANAGED_DEVICE_LEGACY); 361 mController.showUserConsentScreen(); 362 } 363 startFinancedDeviceFlow()364 private void startFinancedDeviceFlow() { 365 mController.setProvisioningMode(PROVISIONING_MODE_FINANCED_DEVICE); 366 mController.continueProvisioningAfterUserConsent(); 367 } 368 369 @Override showFactoryResetDialog(Integer titleId, int messageId)370 public void showFactoryResetDialog(Integer titleId, int messageId) { 371 SimpleDialog.Builder dialogBuilder = new SimpleDialog.Builder() 372 .setTitle(titleId) 373 .setMessage(messageId) 374 .setCancelable(false) 375 .setPositiveButtonMessage(R.string.reset); 376 377 showDialog(dialogBuilder, ERROR_DIALOG_RESET); 378 } 379 380 @Override initiateUi(UiParams uiParams)381 public void initiateUi(UiParams uiParams) { 382 mConsentUiHelper.initiateUi(uiParams); 383 } 384 385 @Override prepareAdminIntegratedFlow(ProvisioningParams params)386 public void prepareAdminIntegratedFlow(ProvisioningParams params) { 387 Intent intent = new Intent(this, LandingActivity.class); 388 WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent); 389 intent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params); 390 startActivityForResult(intent, ADMIN_INTEGRATED_FLOW_PREPARE_REQUEST_CODE); 391 } 392 393 @Override prepareFinancedDeviceFlow(ProvisioningParams params)394 public void prepareFinancedDeviceFlow(ProvisioningParams params) { 395 Intent intent = new Intent(this, FinancedDeviceLandingActivity.class); 396 WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent); 397 intent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params); 398 startActivityForResult(intent, FINANCED_DEVICE_PREPARE_REQUEST_CODE); 399 } 400 401 @Override onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)402 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 403 super.onCreateContextMenu(menu, v, menuInfo); 404 if (v instanceof TextView) { 405 mContextMenuMaker.populateMenuContent(menu, (TextView) v); 406 } 407 } 408 409 @Override showDeleteManagedProfileDialog(ComponentName mdmPackageName, String domainName, int userId)410 public void showDeleteManagedProfileDialog(ComponentName mdmPackageName, String domainName, 411 int userId) { 412 showDialog(() -> DeleteManagedProfileDialog.newInstance(userId, 413 mdmPackageName, domainName), DELETE_MANAGED_PROFILE_DIALOG); 414 } 415 416 @Override onBackPressed()417 public void onBackPressed() { 418 if (mController.getParams().isOrganizationOwnedProvisioning) { 419 showDialog(mUtils.createCancelProvisioningResetDialogBuilder(), 420 BACK_PRESSED_DIALOG_RESET); 421 } else { 422 showDialog(mUtils.createCancelProvisioningDialogBuilder(), 423 BACK_PRESSED_DIALOG_CLOSE_ACTIVITY); 424 } 425 } 426 427 @Override onStart()428 protected void onStart() { 429 super.onStart(); 430 mConsentUiHelper.onStart(); 431 } 432 433 @Override onStop()434 protected void onStop() { 435 super.onStop(); 436 mConsentUiHelper.onStop(); 437 } 438 439 @Override nextAfterUserConsent()440 public void nextAfterUserConsent() { 441 mController.continueProvisioningAfterUserConsent(); 442 } 443 444 @Override initializeLayoutParams(int layoutResourceId, @Nullable Integer headerResourceId, CustomizationParams params)445 public void initializeLayoutParams(int layoutResourceId, @Nullable Integer headerResourceId, 446 CustomizationParams params) { 447 super.initializeLayoutParams(layoutResourceId, headerResourceId, params); 448 } 449 450 /** 451 * Constructs {@link PreProvisioningController} for a given {@link PreProvisioningActivity} 452 */ 453 interface ControllerProvider { 454 /** 455 * Constructs {@link PreProvisioningController} for a given {@link PreProvisioningActivity} 456 */ getInstance(PreProvisioningActivity activity)457 PreProvisioningController getInstance(PreProvisioningActivity activity); 458 } 459 }