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.uiflows; 18 19 import android.annotation.NonNull; 20 import android.app.Activity; 21 import android.app.AlertDialog; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.os.Bundle; 27 import android.os.UserHandle; 28 import android.os.UserManager; 29 import android.provider.Settings; 30 import android.view.View; 31 import android.widget.ImageView; 32 import android.widget.TextView; 33 34 import com.android.managedprovisioning.common.MdmPackageInfo; 35 import com.android.managedprovisioning.common.Utils; 36 import com.android.managedprovisioning.DeleteManagedProfileDialog; 37 import com.android.managedprovisioning.DeviceOwnerProvisioningActivity; 38 import com.android.managedprovisioning.LogoUtils; 39 import com.android.managedprovisioning.ProfileOwnerProvisioningActivity; 40 import com.android.managedprovisioning.ProvisionLogger; 41 import com.android.managedprovisioning.R; 42 import com.android.managedprovisioning.SetupLayoutActivity; 43 import com.android.managedprovisioning.UserConsentDialog; 44 import com.android.managedprovisioning.model.ProvisioningParams; 45 import com.android.managedprovisioning.parser.MessageParser; 46 47 public class PreProvisioningActivity extends SetupLayoutActivity 48 implements UserConsentDialog.ConsentCallback, 49 DeleteManagedProfileDialog.DeleteManagedProfileCallback, 50 PreProvisioningController.Ui { 51 52 protected static final int ENCRYPT_DEVICE_REQUEST_CODE = 1; 53 protected static final int PROVISIONING_REQUEST_CODE = 2; 54 protected static final int WIFI_REQUEST_CODE = 3; 55 protected static final int CHANGE_LAUNCHER_REQUEST_CODE = 4; 56 57 // Note: must match the constant defined in HomeSettings 58 private static final String EXTRA_SUPPORT_MANAGED_PROFILES = "support_managed_profiles"; 59 60 protected PreProvisioningController mController; 61 62 protected TextView mConsentMessageTextView; 63 protected TextView mMdmInfoTextView; 64 65 @Override 66 protected void onCreate(Bundle savedInstanceState) { 67 super.onCreate(savedInstanceState); 68 69 mController = new PreProvisioningController( 70 this, 71 this); 72 73 mController.initiateProvisioning(getIntent(), getCallingPackage()); 74 } 75 76 @Override 77 public void finish() { 78 // The user has backed out of provisioning, so we perform the necessary clean up steps. 79 LogoUtils.cleanUp(this); 80 EncryptionController.getInstance(this).cancelEncryptionReminder(); 81 super.finish(); 82 } 83 84 @Override 85 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 86 if (requestCode == ENCRYPT_DEVICE_REQUEST_CODE) { 87 if (resultCode == RESULT_CANCELED) { 88 ProvisionLogger.loge("User canceled device encryption."); 89 setResult(Activity.RESULT_CANCELED); 90 finish(); 91 } 92 } else if (requestCode == PROVISIONING_REQUEST_CODE) { 93 setResult(resultCode); 94 finish(); 95 } else if (requestCode == CHANGE_LAUNCHER_REQUEST_CODE) { 96 if (!mUtils.currentLauncherSupportsManagedProfiles(this)) { 97 showCurrentLauncherInvalid(); 98 } else { 99 startProfileOwnerProvisioning(mController.getParams()); 100 } 101 } else if (requestCode == WIFI_REQUEST_CODE) { 102 if (resultCode == RESULT_CANCELED) { 103 ProvisionLogger.loge("User canceled wifi picking."); 104 setResult(RESULT_CANCELED); 105 finish(); 106 } else if (resultCode == RESULT_OK) { 107 ProvisionLogger.logd("Wifi request result is OK"); 108 if (mUtils.isConnectedToWifi(this)) { 109 mController.askForConsentOrStartDeviceOwnerProvisioning(); 110 } else { 111 requestWifiPick(); 112 } 113 } 114 } else { 115 ProvisionLogger.logw("Unknown result code :" + resultCode); 116 } 117 } 118 119 @Override 120 public void showErrorAndClose(int resourceId, String logText) { 121 ProvisionLogger.loge(logText); 122 new AlertDialog.Builder(this) 123 .setTitle(R.string.provisioning_error_title) 124 .setMessage(resourceId) 125 .setCancelable(false) 126 .setPositiveButton(R.string.device_owner_error_ok, 127 new DialogInterface.OnClickListener() { 128 @Override 129 public void onClick(DialogInterface dialog,int id) { 130 // Close activity 131 PreProvisioningActivity.this.setResult( 132 Activity.RESULT_CANCELED); 133 PreProvisioningActivity.this.finish(); 134 } 135 }) 136 .show(); 137 } 138 139 @Override 140 public void requestEncryption(ProvisioningParams params) { 141 Intent encryptIntent = new Intent(this, EncryptDeviceActivity.class); 142 encryptIntent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params); 143 startActivityForResult(encryptIntent, ENCRYPT_DEVICE_REQUEST_CODE); 144 } 145 146 @Override 147 public void requestWifiPick() { 148 startActivityForResult(mUtils.getWifiPickIntent(), WIFI_REQUEST_CODE); 149 } 150 151 @Override 152 public void showCurrentLauncherInvalid() { 153 new AlertDialog.Builder(this) 154 .setCancelable(false) 155 .setMessage(R.string.managed_provisioning_not_supported_by_launcher) 156 .setNegativeButton(R.string.cancel_provisioning, 157 new DialogInterface.OnClickListener() { 158 @Override 159 public void onClick(DialogInterface dialog,int id) { 160 dialog.dismiss(); 161 setResult(Activity.RESULT_CANCELED); 162 finish(); 163 } 164 }) 165 .setPositiveButton(R.string.pick_launcher, 166 new DialogInterface.OnClickListener() { 167 @Override 168 public void onClick(DialogInterface dialog,int id) { 169 requestLauncherPick(); 170 } 171 }) 172 .show(); 173 } 174 175 private void requestLauncherPick() { 176 Intent changeLauncherIntent = new Intent(Settings.ACTION_HOME_SETTINGS); 177 changeLauncherIntent.putExtra(EXTRA_SUPPORT_MANAGED_PROFILES, true); 178 startActivityForResult(changeLauncherIntent, CHANGE_LAUNCHER_REQUEST_CODE); 179 } 180 181 @Override 182 public void startDeviceOwnerProvisioning(int userId, ProvisioningParams params) { 183 Intent intent = new Intent(this, DeviceOwnerProvisioningActivity.class); 184 intent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params); 185 startActivityForResultAsUser(intent, PROVISIONING_REQUEST_CODE, new UserHandle(userId)); 186 // Set cross-fade transition animation into the interstitial progress activity. 187 overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); 188 } 189 190 @Override 191 public void startProfileOwnerProvisioning(ProvisioningParams params) { 192 Intent intent = new Intent(this, ProfileOwnerProvisioningActivity.class); 193 intent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, params); 194 startActivityForResult(intent, PROVISIONING_REQUEST_CODE); 195 // Set cross-fade transition animation into the interstitial progress activity. 196 overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); 197 } 198 199 @Override 200 public void initiateUi(int headerRes, int titleRes, int consentRes, int mdmInfoRes, 201 ProvisioningParams params) { 202 // Setup the UI. 203 initializeLayoutParams(R.layout.user_consent, headerRes, false); 204 configureNavigationButtons(R.string.next, View.INVISIBLE, View.VISIBLE); 205 206 mConsentMessageTextView = (TextView) findViewById(R.id.user_consent_message); 207 mMdmInfoTextView = (TextView) findViewById(R.id.mdm_info_message); 208 209 mConsentMessageTextView.setText(consentRes); 210 mMdmInfoTextView.setText(mdmInfoRes); 211 212 setMdmIconAndLabel(params.inferDeviceAdminPackageName()); 213 214 maybeSetLogoAndMainColor(params.mainColor); 215 mNextButton.setVisibility(View.VISIBLE); 216 217 setTitle(titleRes); 218 } 219 220 private void setMdmIconAndLabel(@NonNull String packageName) { 221 MdmPackageInfo packageInfo = MdmPackageInfo.createFromPackageName(this, packageName); 222 TextView deviceManagerName = (TextView) findViewById(R.id.device_manager_name); 223 if (packageInfo != null) { 224 String appLabel = packageInfo.appLabel; 225 ImageView imageView = (ImageView) findViewById(R.id.device_manager_icon_view); 226 imageView.setImageDrawable(packageInfo.packageIcon); 227 imageView.setContentDescription( 228 getResources().getString(R.string.mdm_icon_label, appLabel)); 229 230 deviceManagerName.setText(appLabel); 231 } else { 232 // During provisioning from trusted source, the package is not actually on the device 233 // yet, so show a default information. 234 deviceManagerName.setText(packageName); 235 } 236 } 237 238 @Override 239 public void showUserConsentDialog(ProvisioningParams params, 240 boolean isProfileOwnerProvisioning) { 241 UserConsentDialog dialog; 242 if (isProfileOwnerProvisioning) { 243 dialog = UserConsentDialog.newProfileOwnerInstance(); 244 } else { 245 dialog = UserConsentDialog.newDeviceOwnerInstance(!params.startedByTrustedSource); 246 } 247 dialog.show(getFragmentManager(), "UserConsentDialogFragment"); 248 } 249 250 /** 251 * Callback for successful user consent request. 252 */ 253 @Override 254 public void onDialogConsent() { 255 // Right after user consent, provisioning will be started. To avoid talkback reading out 256 // the activity title in the time this activity briefly comes back to the foreground, we 257 // remove the title. 258 setTitle(""); 259 260 mController.continueProvisioningAfterUserConsent(); 261 } 262 263 /** 264 * Callback for cancelled user consent request. 265 */ 266 @Override 267 public void onDialogCancel() { 268 // only show special UI for device owner provisioning. 269 if (mController.isProfileOwnerProvisioning()) { 270 return; 271 } 272 273 // For Nfc provisioning, we automatically show the user consent dialog if applicable. 274 // If the user then decides to cancel, we should finish the entire activity and exit. 275 // For other cases, dismissing the consent dialog will lead back to PreProvisioningActivity, 276 // where we show another dialog asking for user confirmation to cancel the setup and 277 // factory reset the device. 278 if (mController.getParams().startedByTrustedSource) { 279 setResult(RESULT_CANCELED); 280 finish(); 281 } else { 282 new AlertDialog.Builder(this) 283 .setTitle(R.string.cancel_setup_and_factory_reset_dialog_title) 284 .setMessage(R.string.cancel_setup_and_factory_reset_dialog_msg) 285 .setNegativeButton(R.string.cancel_setup_and_factory_reset_dialog_cancel, null) 286 .setPositiveButton(R.string.cancel_setup_and_factory_reset_dialog_ok, 287 new AlertDialog.OnClickListener() { 288 @Override 289 public void onClick(DialogInterface dialog, int id) { 290 mUtils.sendFactoryResetBroadcast( 291 PreProvisioningActivity.this, 292 "Device owner setup cancelled"); 293 } 294 }) 295 .show(); 296 } 297 } 298 299 @Override 300 public void showDeleteManagedProfileDialog(ComponentName mdmPackageName, String domainName, 301 int userId) { 302 DeleteManagedProfileDialog.newInstance(userId, mdmPackageName, domainName) 303 .show(getFragmentManager(), "DeleteManagedProfileDialogFragment"); 304 } 305 306 /** 307 * Callback for user agreeing to remove existing managed profile. 308 */ 309 @Override 310 public void onRemoveProfileApproval(int existingManagedProfileUserId) { 311 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); 312 userManager.removeUser(existingManagedProfileUserId); 313 } 314 315 /** 316 * Callback for cancelled deletion of existing managed profile. 317 */ 318 @Override 319 public void onRemoveProfileCancel() { 320 setResult(Activity.RESULT_CANCELED); 321 finish(); 322 } 323 324 /** 325 * When the user backs out of creating a managed profile, show a dialog to double check. 326 */ 327 @Override 328 public void onBackPressed() { 329 if (!mController.isProfileOwnerProvisioning()) { 330 super.onBackPressed(); 331 return; 332 } 333 // TODO: Update strings for managed user case 334 new AlertDialog.Builder(this) 335 .setTitle(R.string.work_profile_setup_later_title) 336 .setMessage(R.string.work_profile_setup_later_message) 337 .setCancelable(false) 338 .setPositiveButton(R.string.work_profile_setup_stop, 339 new DialogInterface.OnClickListener() { 340 @Override 341 public void onClick(DialogInterface dialog,int id) { 342 PreProvisioningActivity.this.setResult( 343 Activity.RESULT_CANCELED); 344 PreProvisioningActivity.this.finish(); 345 } 346 }) 347 .setNegativeButton(R.string.work_profile_setup_continue, 348 new DialogInterface.OnClickListener() { 349 @Override 350 public void onClick(DialogInterface dialog, int id) { 351 // user chose to continue. Do nothing 352 } 353 }) 354 .show(); 355 } 356 357 @Override 358 public void onNavigateNext() { 359 mController.afterNavigateNext(); 360 } 361 } 362