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