1 /* 2 * Copyright 2019, 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.provisioning; 18 19 import android.annotation.IntDef; 20 import android.app.Activity; 21 import android.app.DialogFragment; 22 import android.content.Intent; 23 import android.os.Bundle; 24 25 import androidx.annotation.VisibleForTesting; 26 27 import com.android.managedprovisioning.ManagedProvisioningScreens; 28 import com.android.managedprovisioning.R; 29 import com.android.managedprovisioning.common.DialogBuilder; 30 import com.android.managedprovisioning.common.ProvisionLogger; 31 import com.android.managedprovisioning.common.SettingsFacade; 32 import com.android.managedprovisioning.common.SetupGlifLayoutActivity; 33 import com.android.managedprovisioning.common.SimpleDialog; 34 import com.android.managedprovisioning.common.ThemeHelper; 35 import com.android.managedprovisioning.common.Utils; 36 import com.android.managedprovisioning.model.ProvisioningParams; 37 import com.android.managedprovisioning.util.LazyStringResource; 38 39 import com.google.android.setupcompat.logging.ScreenKey; 40 import com.google.android.setupcompat.logging.SetupMetric; 41 import com.google.android.setupcompat.logging.SetupMetricsLogger; 42 import com.google.android.setupcompat.util.WizardManagerHelper; 43 44 import java.lang.annotation.Retention; 45 import java.lang.annotation.RetentionPolicy; 46 47 /** 48 * Abstract class for provisioning activity. 49 * 50 * <p>This activity registers for updates of the provisioning process from the 51 * {@link ProvisioningManager}. It shows progress updates as provisioning progresses and handles 52 * showing of cancel and error dialogs.</p> 53 */ 54 // TODO(b/123288153): Rearrange provisioning activity, manager, controller classes. 55 public abstract class AbstractProvisioningActivity extends SetupGlifLayoutActivity 56 implements SimpleDialog.SimpleDialogListener, ProvisioningManagerCallback { 57 58 private static final String KEY_ACTIVITY_STATE = "activity-state"; 59 60 static final int STATE_PROVISIONING_INTIIALIZING = 1; 61 static final int STATE_PROVISIONING_STARTED = 2; 62 static final int STATE_PROVISIONING_COMPLETED = 3; 63 static final int STATE_PROVISIONING_FINALIZED = 4; 64 static final int SETUP_METRIC_DEFAULT_ERROR_CODE = -1; 65 protected ScreenKey mScreenKey; 66 protected String setupMetricScreenName= "DefaultScreenName"; 67 68 69 @Retention(RetentionPolicy.SOURCE) 70 @IntDef({STATE_PROVISIONING_INTIIALIZING, 71 STATE_PROVISIONING_STARTED, 72 STATE_PROVISIONING_COMPLETED, 73 STATE_PROVISIONING_FINALIZED}) 74 private @interface ProvisioningState {} 75 76 @VisibleForTesting static final String ERROR_DIALOG_OK = "ErrorDialogOk"; 77 @VisibleForTesting static final String ERROR_DIALOG_RESET = "ErrorDialogReset"; 78 @VisibleForTesting static final String CANCEL_PROVISIONING_DIALOG_OK 79 = "CancelProvisioningDialogOk"; 80 @VisibleForTesting static final String CANCEL_PROVISIONING_DIALOG_RESET 81 = "CancelProvisioningDialogReset"; 82 83 protected ProvisioningParams mParams; 84 protected @ProvisioningState int mState; 85 86 @VisibleForTesting AbstractProvisioningActivity( Utils utils, SettingsFacade settingsFacade, ThemeHelper themeHelper)87 protected AbstractProvisioningActivity( 88 Utils utils, SettingsFacade settingsFacade, ThemeHelper themeHelper) { 89 super(utils, settingsFacade, themeHelper); 90 } 91 92 // Lazily initialize ProvisioningManager, since we can't call in ProvisioningManager.getInstance 93 // in constructor as base context is not available in constructor. getProvisioningManager()94 protected abstract ProvisioningManagerInterface getProvisioningManager(); 95 // Show the dialog when user press back button while provisioning. decideCancelProvisioningDialog()96 protected abstract void decideCancelProvisioningDialog(); 97 98 @Override onCreate(Bundle savedInstanceState)99 protected void onCreate(Bundle savedInstanceState) { 100 // initialize params so they're accessible for prechecks in onCreate 101 mParams = getIntent().getParcelableExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS); 102 mScreenKey = ScreenKey.of(setupMetricScreenName, this); 103 super.onCreate(savedInstanceState); 104 105 if (savedInstanceState != null) { 106 mState = savedInstanceState.getInt(KEY_ACTIVITY_STATE, 107 STATE_PROVISIONING_INTIIALIZING); 108 } else { 109 mState = STATE_PROVISIONING_INTIIALIZING; 110 } 111 112 if (mState == STATE_PROVISIONING_INTIIALIZING) { 113 getProvisioningManager().maybeStartProvisioning(mParams); 114 mState = STATE_PROVISIONING_STARTED; 115 } 116 } 117 118 @Override onSaveInstanceState(Bundle outState)119 protected void onSaveInstanceState(Bundle outState) { 120 super.onSaveInstanceState(outState); 121 outState.putInt(KEY_ACTIVITY_STATE, mState); 122 } 123 124 @Override onResume()125 protected void onResume() { 126 super.onResume(); 127 if (!isAnyDialogAdded()) { 128 getProvisioningManager().registerListener(this); 129 } 130 } 131 isAnyDialogAdded()132 private boolean isAnyDialogAdded() { 133 return isDialogAdded(ERROR_DIALOG_OK) 134 || isDialogAdded(ERROR_DIALOG_RESET) 135 || isDialogAdded(CANCEL_PROVISIONING_DIALOG_OK) 136 || isDialogAdded(CANCEL_PROVISIONING_DIALOG_RESET); 137 } 138 139 @Override onPause()140 public void onPause() { 141 getProvisioningManager().unregisterListener(this); 142 super.onPause(); 143 } 144 145 @Override onBackPressed()146 public void onBackPressed() { 147 decideCancelProvisioningDialog(); 148 } 149 showCancelProvisioningDialog(boolean resetRequired)150 protected void showCancelProvisioningDialog(boolean resetRequired) { 151 if (resetRequired) { 152 showDialog( 153 mUtils.createCancelProvisioningResetDialogBuilder( 154 getApplicationContext()), CANCEL_PROVISIONING_DIALOG_RESET); 155 } else { 156 showDialog(mUtils.createCancelProvisioningDialogBuilder(), 157 CANCEL_PROVISIONING_DIALOG_OK); 158 } 159 } 160 startResetActivity()161 protected void startResetActivity() { 162 final Intent intent = 163 new Intent(this, getActivityForScreen(ManagedProvisioningScreens.RESET_DEVICE)); 164 WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent); 165 getTransitionHelper().startActivityWithTransition(this, intent); 166 } 167 168 @Override error(int titleId, int messageId, boolean resetRequired)169 public void error(int titleId, int messageId, boolean resetRequired) { 170 if (messageId == R.string.fully_managed_device_unsupported_DPC_in_headless_mode_subheader) { 171 ProvisionLogger.logd("This admin app does not support fully managed mode on " + 172 "headless system user devices"); 173 startResetActivity(); 174 return; 175 } 176 SimpleDialog.Builder dialogBuilder = new SimpleDialog.Builder() 177 .setTitle(titleId) 178 .setMessage(messageId) 179 .setCancelable(false) 180 .setPositiveButtonMessage(resetRequired 181 ? R.string.reset : android.R.string.ok); 182 183 showDialog(dialogBuilder, resetRequired ? ERROR_DIALOG_RESET : ERROR_DIALOG_OK); 184 } 185 186 @Override error(int titleId, String errorMessage, boolean resetRequired)187 public void error(int titleId, String errorMessage, boolean resetRequired) { 188 if (errorMessage.equals(getString(R.string.fully_managed_device_unsupported_DPC_in_headless_mode_subheader))) { 189 ProvisionLogger.logd("This admin app does not support fully managed mode on " + 190 "headless system user devices"); 191 startResetActivity(); 192 return; 193 } 194 SimpleDialog.Builder dialogBuilder = 195 new SimpleDialog.Builder() 196 .setTitle(titleId) 197 .setMessage(LazyStringResource.of(errorMessage)) 198 .setCancelable(false) 199 .setPositiveButtonMessage( 200 resetRequired ? R.string.reset : android.R.string.ok); 201 202 showDialog(dialogBuilder, resetRequired ? ERROR_DIALOG_RESET : ERROR_DIALOG_OK); 203 } 204 205 @Override showDialog(DialogBuilder builder, String tag)206 protected void showDialog(DialogBuilder builder, String tag) { 207 SetupMetricsLogger.logMetrics(this, mScreenKey, 208 SetupMetric.ofError(setupMetricScreenName + ": " + tag, 209 SETUP_METRIC_DEFAULT_ERROR_CODE )); 210 // Whenever a dialog is shown, stop listening for further updates 211 getProvisioningManager().unregisterListener(this); 212 super.showDialog(builder, tag); 213 } 214 onProvisioningAborted()215 private void onProvisioningAborted() { 216 setResult(Activity.RESULT_CANCELED); 217 getTransitionHelper().finishActivity(this); 218 } 219 220 @Override onNegativeButtonClick(DialogFragment dialog)221 public void onNegativeButtonClick(DialogFragment dialog) { 222 switch (dialog.getTag()) { 223 case CANCEL_PROVISIONING_DIALOG_OK: 224 case CANCEL_PROVISIONING_DIALOG_RESET: 225 dialog.dismiss(); 226 break; 227 default: 228 SimpleDialog.throwButtonClickHandlerNotImplemented(dialog); 229 } 230 getProvisioningManager().registerListener(this); 231 } 232 233 @Override onPositiveButtonClick(DialogFragment dialog)234 public void onPositiveButtonClick(DialogFragment dialog) { 235 switch (dialog.getTag()) { 236 case CANCEL_PROVISIONING_DIALOG_OK: 237 getProvisioningManager().cancelProvisioning(); 238 onProvisioningAborted(); 239 break; 240 case CANCEL_PROVISIONING_DIALOG_RESET: 241 getUtils().factoryReset(this, "Provisioning cancelled by user"); 242 onProvisioningAborted(); 243 break; 244 case ERROR_DIALOG_OK: 245 onProvisioningAborted(); 246 break; 247 case ERROR_DIALOG_RESET: 248 getUtils().factoryReset(this, "Error during provisioning"); 249 onProvisioningAborted(); 250 break; 251 default: 252 SimpleDialog.throwButtonClickHandlerNotImplemented(dialog); 253 } 254 } 255 } 256