1 /* 2 * Copyright 2015, 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; 18 19 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE; 20 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE; 21 import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED; 22 23 import android.app.Activity; 24 import android.app.AlertDialog; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.os.Bundle; 29 import android.os.SystemProperties; 30 import android.provider.Settings.Global; 31 import android.provider.Settings.Secure; 32 import android.service.persistentdata.PersistentDataBlockManager; 33 import android.text.TextUtils; 34 import android.view.View; 35 import android.widget.ImageView; 36 import android.widget.TextView; 37 38 import com.android.managedprovisioning.task.AddWifiNetworkTask; 39 import com.android.managedprovisioning.Utils.IllegalProvisioningArgumentException; 40 import com.android.managedprovisioning.Utils.MdmPackageInfo; 41 42 /** 43 * This activity makes necessary checks before starting {@link DeviceOwnerProvisioningActivity}. 44 * It checks if the device or user is already setup. It makes sure the device is encrypted, the 45 * provisioning intent extras are valid and the device is connected to a wifi network. 46 */ 47 public class DeviceOwnerPreProvisioningActivity extends SetupLayoutActivity 48 implements UserConsentDialog.ConsentCallback { 49 private static final boolean DEBUG = false; // To control logging. 50 51 private static final String KEY_USER_CONSENTED = "user_consented"; 52 53 private static final int ENCRYPT_DEVICE_REQUEST_CODE = 1; 54 private static final int WIFI_REQUEST_CODE = 2; 55 private static final int PROVISIONING_REQUEST_CODE = 3; 56 57 // Indicates whether user consented by clicking on positive button of consent dialog. 58 private boolean mUserConsented = false; 59 60 // Params that will be used after user consent. 61 // Extracted from the starting intent. 62 private ProvisioningParams mParams; 63 64 // Legacy action, internal only, but that we still want to support. 65 static final String LEGACY_ACTION_PROVISION_MANAGED_DEVICE 66 = "com.android.managedprovisioning.ACTION_PROVISION_MANAGED_DEVICE"; 67 68 /** 69 * Action used to start DeviceOwnerPreProvisioningActivity on a secondary user. 70 */ 71 protected static final String ACTION_PROVISION_SECONDARY_USER 72 = "com.android.managedprovisioning.ACTION_PROVISION_SECONDARY_USER"; 73 74 @Override 75 public void onCreate(Bundle savedInstanceState) { 76 super.onCreate(savedInstanceState); 77 if (DEBUG) ProvisionLogger.logd("DeviceOwnerPreProvisioningActivity ONCREATE"); 78 79 if (savedInstanceState != null) { 80 mUserConsented = savedInstanceState.getBoolean(KEY_USER_CONSENTED, false); 81 } 82 83 // Setup the UI. 84 initializeLayoutParams(R.layout.user_consent, R.string.setup_work_device, false); 85 configureNavigationButtons(R.string.set_up, View.INVISIBLE, View.VISIBLE); 86 87 // Check whether we have already provisioned this user. 88 if (Utils.isCurrentUserOwner()) { 89 int provisioned = 90 Global.getInt(getContentResolver(), Global.DEVICE_PROVISIONED, 0 /*defaults*/); 91 if (provisioned != 0) { 92 showErrorAndClose(R.string.device_owner_error_already_provisioned, 93 "Device already provisioned."); 94 return; 95 } 96 } else { 97 int provisioned = 98 Secure.getInt(getContentResolver(), Secure.USER_SETUP_COMPLETE, 0 /*default*/); 99 if (provisioned != 0) { 100 showErrorAndClose(R.string.device_owner_error_already_provisioned_user, 101 "User already provisioned."); 102 return; 103 } 104 } 105 106 if (factoryResetProtected()) { 107 showErrorAndClose(R.string.device_owner_error_frp, 108 "Factory reset protection blocks provisioning."); 109 return; 110 } 111 112 // Parse the incoming intent. 113 MessageParser parser = new MessageParser(); 114 try { 115 mParams = parseIntentAndMaybeVerifyCaller(getIntent(), 116 getPackageName().equals(getCallingPackage()), parser); 117 } catch (Utils.IllegalProvisioningArgumentException e) { 118 showErrorAndClose(R.string.device_owner_error_general, 119 e.getMessage()); 120 return; 121 } 122 123 // Ask to encrypt the device before proceeding 124 if (!(EncryptDeviceActivity.isDeviceEncrypted() 125 || SystemProperties.getBoolean("persist.sys.no_req_encrypt", false) 126 || mParams.skipEncryption)) { 127 requestEncryption(parser, mParams); 128 return; 129 // System will reboot. Bootreminder will restart this activity. 130 } 131 132 // Have the user pick a wifi network if necessary. 133 if (!NetworkMonitor.isConnectedToNetwork(this) 134 && TextUtils.isEmpty(mParams.wifiInfo.ssid) 135 // If a device initializer is installed, this shouldn't run on secondary users. 136 && (!Utils.hasDeviceInitializer(this) || Utils.isCurrentUserOwner())) { 137 requestWifiPick(); 138 return; 139 // Wait for onActivityResult. 140 } 141 askForConsentOrStartProvisioning(); 142 } 143 144 @Override 145 protected void onResume() { 146 super.onResume(); 147 setTitle(R.string.setup_device_start_setup); 148 } 149 150 private ProvisioningParams parseIntentAndMaybeVerifyCaller(Intent intent, boolean trusted, 151 MessageParser parser) throws IllegalProvisioningArgumentException { 152 if (intent.getAction() == null) { 153 throw new IllegalProvisioningArgumentException("Null intent action."); 154 } else if (intent.getAction().equals(ACTION_NDEF_DISCOVERED)) { 155 return parser.parseNfcIntent(intent); 156 } else if (intent.getAction().equals(LEGACY_ACTION_PROVISION_MANAGED_DEVICE)) { 157 return parser.parseNonNfcIntent(intent, trusted); 158 } else if (intent.getAction().equals(ACTION_PROVISION_SECONDARY_USER)) { 159 if (Utils.isCurrentUserOwner()) { 160 throw new IllegalProvisioningArgumentException("Permission denied. " + 161 "Was this activity started for a secondary user?"); 162 } 163 return parser.parseNonNfcIntent(intent, trusted); 164 } else if (intent.getAction().equals(ACTION_PROVISION_MANAGED_DEVICE)) { 165 ProvisioningParams params = parser.parseMinimalistNonNfcIntent(intent); 166 String callingPackage = getCallingPackage(); 167 if (callingPackage == null) { 168 throw new IllegalProvisioningArgumentException("Calling package is null. " + 169 "Was startActivityForResult used to start this activity?"); 170 } 171 if (!callingPackage.equals(params.inferDeviceAdminPackageName())) { 172 throw new IllegalProvisioningArgumentException("Permission denied, " 173 + "calling package tried to set a different package as device owner. "); 174 } 175 return params; 176 } 177 throw new IllegalProvisioningArgumentException("Unknown intent action " 178 + intent.getAction()); 179 } 180 181 private boolean factoryResetProtected() { 182 if (!Utils.isCurrentUserOwner()) { 183 ProvisionLogger.logd("Reset protection check skipped on secondary users."); 184 return false; 185 } 186 187 // If we are started by setup wizard, do not check for factory reset protection since setup 188 // wizard must have already validated it. This avoids provisioning being blocked by FRP 189 // if an account was added in the setup wizard on devices that enforce FRP as soon as the 190 // account is added. 191 if (getIntent() != null) { 192 String action = getIntent().getAction(); 193 if (ACTION_PROVISION_MANAGED_DEVICE.equals(action) || 194 LEGACY_ACTION_PROVISION_MANAGED_DEVICE.equals(action)) { 195 ProvisionLogger.logd("FRP not required if started by SUW"); 196 return false; 197 } 198 } 199 200 PersistentDataBlockManager pdbManager = (PersistentDataBlockManager) 201 getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); 202 if (pdbManager == null) { 203 ProvisionLogger.logd("Reset protection not supported."); 204 return false; 205 } 206 int size = pdbManager.getDataBlockSize(); 207 ProvisionLogger.logd("Data block size: " + size); 208 return size > 0; 209 } 210 211 private void askForConsentOrStartProvisioning() { 212 // If we are started by Nfc and the device supports FRP, we need to ask for user consent 213 // since FRP will not be activated at the end of the flow. 214 if (mParams.startedByNfc && Utils.isFrpSupported(this)) { 215 showUserConsentDialog(); 216 } else if (mUserConsented || mParams.startedByNfc || !Utils.isCurrentUserOwner()) { 217 startDeviceOwnerProvisioning(); 218 } else { 219 showStartProvisioningButton(); 220 TextView consentMessageTextView = (TextView) findViewById(R.id.user_consent_message); 221 consentMessageTextView.setText(R.string.company_controls_device); 222 TextView mdmInfoTextView = (TextView) findViewById(R.id.mdm_info_message); 223 mdmInfoTextView.setText(R.string.the_following_is_your_mdm_for_device); 224 setMdmInfo(); 225 } 226 } 227 228 private void startDeviceOwnerProvisioning() { 229 Intent intent = new Intent(this, DeviceOwnerProvisioningActivity.class); 230 intent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, mParams); 231 startActivityForResult(intent, PROVISIONING_REQUEST_CODE); 232 // Set cross-fade transition animation into the interstitial progress activity. 233 overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); 234 } 235 236 private void showErrorAndClose(int resourceId, String logText) { 237 ProvisionLogger.loge(logText); 238 new AlertDialog.Builder(this) 239 .setTitle(R.string.provisioning_error_title) 240 .setMessage(resourceId) 241 .setCancelable(false) 242 .setPositiveButton(R.string.device_owner_error_ok, 243 new DialogInterface.OnClickListener() { 244 @Override 245 public void onClick(DialogInterface dialog,int id) { 246 // Close activity 247 DeviceOwnerPreProvisioningActivity.this.setResult( 248 Activity.RESULT_CANCELED); 249 DeviceOwnerPreProvisioningActivity.this.finish(); 250 } 251 }) 252 .show(); 253 } 254 255 private void requestEncryption(MessageParser messageParser, ProvisioningParams params) { 256 Intent encryptIntent = new Intent(this, EncryptDeviceActivity.class); 257 258 Bundle resumeExtras = new Bundle(); 259 resumeExtras.putString(EncryptDeviceActivity.EXTRA_RESUME_TARGET, 260 EncryptDeviceActivity.TARGET_DEVICE_OWNER); 261 messageParser.addProvisioningParamsToBundle(resumeExtras, params); 262 263 encryptIntent.putExtra(EncryptDeviceActivity.EXTRA_RESUME, resumeExtras); 264 265 startActivityForResult(encryptIntent, ENCRYPT_DEVICE_REQUEST_CODE); 266 } 267 268 private void requestWifiPick() { 269 startActivityForResult(AddWifiNetworkTask.getWifiPickIntent(), WIFI_REQUEST_CODE); 270 } 271 272 @Override 273 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 274 if (requestCode == ENCRYPT_DEVICE_REQUEST_CODE) { 275 if (resultCode == RESULT_CANCELED) { 276 ProvisionLogger.loge("User canceled device encryption."); 277 setResult(RESULT_CANCELED); 278 finish(); 279 } 280 } else if (requestCode == WIFI_REQUEST_CODE) { 281 if (resultCode == RESULT_CANCELED) { 282 ProvisionLogger.loge("User canceled wifi picking."); 283 setResult(RESULT_CANCELED); 284 finish(); 285 } else if (resultCode == RESULT_OK) { 286 if (DEBUG) ProvisionLogger.logd("Wifi request result is OK"); 287 if (NetworkMonitor.isConnectedToWifi(this)) { 288 askForConsentOrStartProvisioning(); 289 } else { 290 requestWifiPick(); 291 } 292 } 293 } else if (requestCode == PROVISIONING_REQUEST_CODE) { 294 setResult(resultCode); 295 finish(); 296 } 297 } 298 299 private void setMdmInfo() { 300 ImageView imageView = (ImageView) findViewById(R.id.mdm_icon_view); 301 TextView deviceManagerName = (TextView) findViewById(R.id.device_manager_name); 302 String packageName = mParams.inferDeviceAdminPackageName(); 303 MdmPackageInfo packageInfo = Utils.getMdmPackageInfo(getPackageManager(), packageName); 304 if (packageInfo != null) { 305 String appLabel = packageInfo.getAppLabel(); 306 imageView.setImageDrawable(packageInfo.getPackageIcon()); 307 imageView.setContentDescription( 308 getResources().getString(R.string.mdm_icon_label, appLabel)); 309 deviceManagerName.setText(appLabel); 310 } else { 311 // Should never happen. Package will be on the device by now. 312 deviceManagerName.setText(packageName); 313 } 314 } 315 316 @Override 317 protected void onSaveInstanceState(Bundle outState) { 318 outState.putBoolean(KEY_USER_CONSENTED, mUserConsented); 319 } 320 321 private void showStartProvisioningButton() { 322 mNextButton.setVisibility(View.VISIBLE); 323 } 324 325 @Override 326 public void onDialogConsent() { 327 // For accessibility purposes: we need to talk back only the title of the 328 // next screen after user clicks ok. 329 setTitle(""); 330 mUserConsented = true; 331 startDeviceOwnerProvisioning(); 332 } 333 334 @Override 335 public void onDialogCancel() { 336 // For Nfc provisioning, we automatically show the user consent dialog if applicable. 337 // If the user then decides to cancel, we should finish the entire activity and exit. 338 // For other cases, dismissing the consent dialog will lead back to 339 // DeviceOwnerPreProvisioningActivity, and we do nothing here. 340 if (mParams.startedByNfc) { 341 setResult(RESULT_CANCELED); 342 finish(); 343 } 344 } 345 346 @Override 347 public void onNavigateNext() { 348 showUserConsentDialog(); 349 } 350 351 /** 352 * Notify the user that the admin will have full control over the device, 353 * then start provisioning. 354 */ 355 private void showUserConsentDialog() { 356 UserConsentDialog.newInstance(UserConsentDialog.DEVICE_OWNER) 357 .show(getFragmentManager(), "UserConsentDialogFragment"); 358 } 359 } 360