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