1 /* 2 * Copyright (C) 2020 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.car.provision; 18 19 import static android.app.Activity.RESULT_CANCELED; 20 import static android.app.Activity.RESULT_FIRST_USER; 21 import static android.app.Activity.RESULT_OK; 22 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE; 23 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME; 24 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION; 25 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM; 26 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TRIGGER; 27 import static android.app.admin.DevicePolicyManager.PROVISIONING_TRIGGER_QR_CODE; 28 import static android.car.settings.CarSettings.Secure.KEY_ENABLE_INITIAL_NOTICE_SCREEN_TO_USER; 29 import static android.car.settings.CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS; 30 31 import android.Manifest.permission; 32 import android.app.Activity; 33 import android.app.AlertDialog; 34 import android.app.Notification; 35 import android.app.NotificationChannel; 36 import android.app.NotificationManager; 37 import android.app.admin.DevicePolicyManager; 38 import android.content.BroadcastReceiver; 39 import android.content.ComponentName; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.content.IntentFilter; 43 import android.content.pm.PackageInfo; 44 import android.content.pm.PackageManager; 45 import android.os.Bundle; 46 import android.os.SystemProperties; 47 import android.os.UserHandle; 48 import android.os.UserManager; 49 import android.provider.Settings; 50 import android.provider.Settings.SettingNotFoundException; 51 import android.util.Log; 52 import android.view.View; 53 import android.view.WindowInsets; 54 import android.view.WindowInsetsController; 55 import android.widget.ArrayAdapter; 56 import android.widget.Button; 57 import android.widget.Spinner; 58 import android.widget.TextView; 59 60 import com.android.car.setupwizardlib.util.CarDrivingStateMonitor; 61 62 import java.io.FileDescriptor; 63 import java.io.PrintWriter; 64 import java.util.ArrayList; 65 import java.util.List; 66 67 /** 68 * Reference implementeation for a Car SetupWizard. 69 * 70 * <p>Features: 71 * 72 * <ul> 73 * <li>Shows UI where user can confirm setup. 74 * <li>Listen to UX restriction events, so it exits setup when the car moves. 75 * <li>Add option to setup managed-provisioning mode. 76 * <li>Sets car-specific properties. 77 * </ul> 78 * 79 * <p>By default, it doesn't show the UI, unless the {@code persist.dev.car_provision.show_ui} 80 * property is set to {@code true}. For example, you can change it by running something like: 81 <pre><code> 82 adb root 83 adb shell setprop persist.dev.car_provision.show_ui true && \ 84 adb shell pm enable --user cur com.android.car.provision/.DefaultActivity &&\ 85 adb shell settings put secure --user cur user_setup_complete 0 && \ 86 adb shell settings put secure --user 0 user_setup_complete 0 &&\ 87 adb shell settings put global device_provisioned 0 &&\ 88 adb shell rm -f /data/system/device_policies_version &&\ 89 adb shell rm -f /data/system/device_policies.xml &&\ 90 adb shell rm -f /data/system/device_owner_2.xml ;\ 91 adb shell rm -f /data/system/users/`adb shell am get-current-user`/profile_owner.xml 92 adb shell stop && adb shell start 93 <code></pre> 94 */ 95 public final class DefaultActivity extends Activity { 96 97 static final String TAG = "CarProvision"; 98 99 // TODO(b/170333009): copied from ManagedProvisioning app, as they're hidden; 100 private static final String PROVISION_FINALIZATION_INSIDE_SUW = 101 "android.app.action.PROVISION_FINALIZATION_INSIDE_SUW"; 102 private static final int RESULT_CODE_PROFILE_OWNER_SET = 122; 103 private static final int RESULT_CODE_DEVICE_OWNER_SET = 123; 104 105 106 private static final int REQUEST_CODE_STEP1 = 42; 107 private static final int REQUEST_CODE_STEP2_PO = 43; 108 private static final int REQUEST_CODE_STEP2_DO = 44; 109 110 private static final int NOTIFICATION_ID = 108; 111 private static final String IMPORTANCE_DEFAULT_ID = "importance_default"; 112 113 private static final List<DpcInfo> sSupportedDpcApps = new ArrayList<>(2); 114 115 private static final String TEST_DPC_NAME = "TestDPC (downloadable)"; 116 private static final String TEST_DPC_PACKAGE = "com.afwsamples.testdpc"; 117 private static final String TEST_DPC_LEGACY_ACTIVITY = TEST_DPC_PACKAGE 118 + ".SetupManagementLaunchActivity"; 119 private static final String TEST_DPC_RECEIVER = TEST_DPC_PACKAGE 120 + ".DeviceAdminReceiver"; 121 private static final String LOCAL_TEST_DPC_NAME = "TestDPC (local only)"; 122 123 private static final String SHOW_UI_SYSTEM_PROPERTY = "persist.dev.car_provision.show_ui"; 124 125 static { 126 DpcInfo testDpc = new DpcInfo(TEST_DPC_NAME, 127 TEST_DPC_PACKAGE, 128 TEST_DPC_LEGACY_ACTIVITY, 129 TEST_DPC_RECEIVER, 130 "gJD2YwtOiWJHkSMkkIfLRlj-quNqG1fb6v100QmzM9w=", 131 "https://testdpc-latest-apk.appspot.com/preview"); 132 // Locally-built version of the TestDPC 133 DpcInfo localTestDpc = new DpcInfo(LOCAL_TEST_DPC_NAME, 134 TEST_DPC_PACKAGE, 135 TEST_DPC_LEGACY_ACTIVITY, 136 TEST_DPC_RECEIVER, 137 /* checkSum= */ null, 138 /* downloadUrl = */ null); 139 sSupportedDpcApps.add(testDpc); 140 sSupportedDpcApps.add(localTestDpc); 141 } 142 143 private CarDrivingStateMonitor mCarDrivingStateMonitor; 144 145 private TextView mErrorsTextView; 146 private Button mFinishSetupButton; 147 private Button mFactoryResetButton; 148 private Spinner mDpcAppsSpinner; 149 private Button mLegacyProvisioningWorkflowButton; 150 private Button mProvisioningWorkflowButton; 151 152 private final BroadcastReceiver mDrivingStateExitReceiver = new BroadcastReceiver() { 153 @Override 154 public void onReceive(Context context, Intent intent) { 155 Log.d(TAG, "onReceive(): " + intent); 156 exitSetup(); 157 } 158 }; 159 160 @Override onCreate(Bundle icicle)161 protected void onCreate(Bundle icicle) { 162 super.onCreate(icicle); 163 164 int userId = getUserId(); 165 Log.i(TAG, "onCreate() for user " + userId + " Intent: " + getIntent()); 166 167 if (userId == UserHandle.USER_SYSTEM && UserManager.isHeadlessSystemUserMode()) { 168 // System user will be provisioned together with the first non-system user 169 Log.i(TAG, "onCreate(): skipping setup on headless system user"); 170 disableSelfAndFinish(); 171 return; 172 } 173 174 if (!showUi()) { 175 Log.w(TAG, "onCreate(): skipping UI because " + SHOW_UI_SYSTEM_PROPERTY 176 + " was not set to true"); 177 finishSetup(); 178 return; 179 } 180 181 DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class); 182 if (dpm.isDeviceManaged()) { 183 Log.i(TAG, "onCreate(): skipping UI on managed device"); 184 finishSetup(); 185 return; 186 } 187 188 setCarSetupInProgress(true); 189 setContentView(R.layout.default_activity); 190 191 mErrorsTextView = findViewById(R.id.error_message); 192 mFinishSetupButton = findViewById(R.id.finish_setup); 193 mFactoryResetButton = findViewById(R.id.factory_reset); 194 mDpcAppsSpinner = findViewById(R.id.dpc_apps); 195 mLegacyProvisioningWorkflowButton = findViewById(R.id.legacy_provision_workflow); 196 mProvisioningWorkflowButton = findViewById(R.id.provision_workflow); 197 198 mLegacyProvisioningWorkflowButton 199 .setOnClickListener((v) -> launchLegacyProvisioningWorkflow()); 200 mProvisioningWorkflowButton.setOnClickListener((v) -> launchProvisioningWorkflow()); 201 mFinishSetupButton.setOnClickListener((v) -> finishSetup()); 202 mFactoryResetButton.setOnClickListener((v) -> factoryReset()); 203 204 hideSystemUi(); 205 updateUi(); 206 setManagedProvisioning(dpm); 207 startMonitor(); 208 } 209 showUi()210 private boolean showUi() { 211 boolean result = false; 212 try { 213 result = SystemProperties.getBoolean(SHOW_UI_SYSTEM_PROPERTY, false); 214 } catch (Exception e) { 215 Log.w(TAG, "error getting property " + SHOW_UI_SYSTEM_PROPERTY); 216 } 217 return result; 218 } 219 startMonitor()220 private void startMonitor() { 221 Log.d(TAG, "startMonitor()"); 222 registerReceiver(mDrivingStateExitReceiver, 223 new IntentFilter(CarDrivingStateMonitor.EXIT_BROADCAST_ACTION), 224 permission.DISPATCH_PROVISIONING_MESSAGE, /* scheduler= */ null, 225 Context.RECEIVER_EXPORTED); 226 227 mCarDrivingStateMonitor = CarDrivingStateMonitor.get(this); 228 mCarDrivingStateMonitor.startMonitor(); 229 } 230 231 @Override finish()232 public void finish() { 233 Log.i(TAG, "finish() for user " + getUserId()); 234 235 stopMonitor(); 236 237 super.finish(); 238 }; 239 240 @Override dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args)241 public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) { 242 if (args == null || args.length == 0) { 243 showDpcs(pw); 244 showHelp(pw); 245 return; 246 } 247 248 if (args[0].equals("--help")) { 249 showHelp(pw); 250 return; 251 } 252 253 addDpc(pw, args); 254 }; 255 showDpcs(PrintWriter pw)256 private void showDpcs(PrintWriter pw) { 257 pw.printf("%d DPCs\n", sSupportedDpcApps.size()); 258 sSupportedDpcApps.forEach((dpc) -> pw.printf("\t%s\n", dpc)); 259 } 260 showHelp(PrintWriter pw)261 private void showHelp(PrintWriter pw) { 262 pw.println("\nTo add a new DPC, use: --name name --package-name package-name" 263 + "--receiver-name receiver-name [--legacy-activity-name legacy-activity-name] " 264 + "[--checksum checksum] [--download-url download-url]"); 265 } 266 addDpc(PrintWriter pw, String[] args)267 private void addDpc(PrintWriter pw, String[] args) { 268 String name = null; 269 String packageName = null; 270 String legacyActivityName = null; 271 String receiverName = null; 272 String checkSum = null; 273 String downloadUrl = null; 274 275 for (int i = 0; i < args.length; i++) { 276 try { 277 switch (args[i]) { 278 case "--name": 279 name = args[++i]; 280 break; 281 case "--package-name": 282 packageName = args[++i]; 283 break; 284 case "--legacy-activity-name": 285 legacyActivityName = args[++i]; 286 break; 287 case "--receiver-name": 288 receiverName = args[++i]; 289 break; 290 case "--checksum": 291 checkSum = args[++i]; 292 break; 293 case "--download-url": 294 downloadUrl = args[++i]; 295 break; 296 default: 297 pw.printf("Invalid option at index %d: %s\n", i, args[i]); 298 return; 299 } 300 } catch (Exception e) { 301 // most likely a missing arg... 302 pw.printf("Error handing arg %d: %s\n", i, e); 303 return; 304 } 305 } 306 307 DpcInfo dpc = new DpcInfo(name, packageName, legacyActivityName, receiverName, checkSum, 308 downloadUrl); 309 Log.i(TAG, "Adding new DPC from dump(): " + dpc); 310 sSupportedDpcApps.add(dpc); 311 pw.printf("Added new DPC: %s\n", dpc); 312 313 updateUi(); 314 } 315 stopMonitor()316 private void stopMonitor() { 317 Log.d(TAG, "stopMonitor()"); 318 319 if (mCarDrivingStateMonitor == null) { 320 // Happens when device is managed (and startMonitor() is skipped) 321 Log.d(TAG, "Already stopped (or never stopped)"); 322 return; 323 } 324 325 if (mDrivingStateExitReceiver != null) { 326 unregisterReceiver(mDrivingStateExitReceiver); 327 } 328 329 mCarDrivingStateMonitor.stopMonitor(); 330 mCarDrivingStateMonitor = null; 331 } 332 hideSystemUi()333 private void hideSystemUi() { 334 WindowInsetsController insetsController = getWindow().getDecorView() 335 .getWindowInsetsController(); 336 if (insetsController == null) { 337 Log.w(TAG, "No insets controller"); 338 return; 339 } 340 Log.d(TAG, "Hiding the system UI bars"); 341 insetsController.hide(WindowInsets.Type.navigationBars()); 342 } 343 updateUi()344 private void updateUi() { 345 String[] appNames = new String[sSupportedDpcApps.size()]; 346 for (int i = 0; i < sSupportedDpcApps.size(); i++) { 347 appNames[i] = sSupportedDpcApps.get(i).name; 348 } 349 mDpcAppsSpinner.setAdapter(new ArrayAdapter<String>(this, 350 android.R.layout.simple_spinner_item, appNames)); 351 mDpcAppsSpinner.setSelection(appNames.length - 1); 352 } 353 setManagedProvisioning(DevicePolicyManager dpm)354 private void setManagedProvisioning(DevicePolicyManager dpm) { 355 if (!getPackageManager() 356 .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) { 357 Log.i(TAG, "Disabling provisioning buttons because device does not have the " 358 + PackageManager.FEATURE_DEVICE_ADMIN + " feature"); 359 return; 360 } 361 if (!dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE)) { 362 Log.w(TAG, "Disabling provisioning buttons because device cannot be provisioned - " 363 + "it can only be set on first boot"); 364 return; 365 } 366 367 mProvisioningWorkflowButton.setEnabled(true); 368 mLegacyProvisioningWorkflowButton.setEnabled(true); 369 } 370 checkDpcAppExists(String dpcApp)371 private boolean checkDpcAppExists(String dpcApp) { 372 if (!checkAppExists(dpcApp, UserHandle.USER_SYSTEM)) return false; 373 if (!checkAppExists(dpcApp, getUserId())) return false; 374 return true; 375 } 376 checkAppExists(String app, int userId)377 private boolean checkAppExists(String app, int userId) { 378 Log.d(TAG, "Checking if " + app + " exits for user " + userId); 379 try { 380 PackageInfo info = getPackageManager().getPackageInfoAsUser(app, /* flags= */ 0, 381 userId); 382 if (info == null) { 383 Log.i(TAG, "No app " + app + " for user " + userId); 384 return false; 385 } 386 Log.d(TAG, "Found it: " + info); 387 return true; 388 } catch (PackageManager.NameNotFoundException e) { 389 return false; 390 } catch (Exception e) { 391 Log.e(TAG, "Error checking if " + app + " exists for user " + userId, e); 392 return false; 393 } 394 } 395 finishSetup()396 private void finishSetup() { 397 Log.i(TAG, "finishing setup for user " + getUserId()); 398 provisionUserAndDevice(); 399 disableSelfAndFinish(); 400 } 401 factoryReset()402 private void factoryReset() { 403 new AlertDialog.Builder(this).setMessage(R.string.factory_reset_warning) 404 .setPositiveButton(android.R.string.ok, (d, w)->sendFactoryResetIntent()) 405 .show(); 406 } 407 sendFactoryResetIntent()408 private void sendFactoryResetIntent() { 409 provisionUserAndDevice(); 410 411 Intent intent = new Intent(Intent.ACTION_FACTORY_RESET); 412 intent.setPackage("android"); 413 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 414 intent.putExtra(Intent.EXTRA_REASON, "Requested by user on SUW"); 415 416 Log.i(TAG, "factory resetting device with intent " + intent); 417 sendBroadcast(intent); 418 419 disableSelfAndFinish(); 420 } 421 provisionUserAndDevice()422 private void provisionUserAndDevice() { 423 Log.d(TAG, "setting Settings properties"); 424 // Add a persistent setting to allow other apps to know the device has been provisioned. 425 if (!isDeviceProvisioned()) { 426 Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1); 427 } 428 429 maybeMarkSystemUserSetupComplete(); 430 Log.v(TAG, "Marking USER_SETUP_COMPLETE for user " + getUserId()); 431 markUserSetupComplete(this); 432 433 // Set car-specific properties 434 setCarSetupInProgress(false); 435 Settings.Secure.putInt(getContentResolver(), KEY_ENABLE_INITIAL_NOTICE_SCREEN_TO_USER, 0); 436 } 437 isDeviceProvisioned()438 private boolean isDeviceProvisioned() { 439 try { 440 return Settings.Global.getInt(getContentResolver(), 441 Settings.Global.DEVICE_PROVISIONED) == 1; 442 } catch (SettingNotFoundException e) { 443 Log.wtf(TAG, "DEVICE_PROVISIONED is not found."); 444 return false; 445 } 446 } 447 isUserSetupComplete(Context context)448 private boolean isUserSetupComplete(Context context) { 449 return Settings.Secure.getInt(context.getContentResolver(), 450 Settings.Secure.USER_SETUP_COMPLETE, /* default= */ 0) == 1; 451 } 452 maybeMarkSystemUserSetupComplete()453 private void maybeMarkSystemUserSetupComplete() { 454 Context systemUserContext = getApplicationContext().createContextAsUser( 455 UserHandle.SYSTEM, /* flags= */ 0); 456 if (!isUserSetupComplete(systemUserContext) && getUserId() != UserHandle.USER_SYSTEM 457 && UserManager.isHeadlessSystemUserMode()) { 458 Log.v(TAG, "Marking USER_SETUP_COMPLETE for system user"); 459 markUserSetupComplete(systemUserContext); 460 } 461 } 462 setCarSetupInProgress(boolean inProgress)463 private void setCarSetupInProgress(boolean inProgress) { 464 Settings.Secure.putInt(getContentResolver(), KEY_SETUP_WIZARD_IN_PROGRESS, 465 inProgress ? 1 : 0); 466 } 467 markUserSetupComplete(Context context)468 private void markUserSetupComplete(Context context) { 469 Settings.Secure.putInt(context.getContentResolver(), 470 Settings.Secure.USER_SETUP_COMPLETE, 1); 471 } 472 exitSetup()473 private void exitSetup() { 474 Log.d(TAG, "exiting setup early for user " + getUserId()); 475 provisionUserAndDevice(); 476 notifySetupExited(); 477 disableSelfAndFinish(); 478 } 479 notifySetupExited()480 private void notifySetupExited() { 481 Log.d(TAG, "Sending exited setup notification"); 482 483 NotificationManager notificationMgr = getSystemService(NotificationManager.class); 484 notificationMgr.createNotificationChannel(new NotificationChannel( 485 IMPORTANCE_DEFAULT_ID, "Importance Default", 486 NotificationManager.IMPORTANCE_DEFAULT)); 487 Notification notification = new Notification 488 .Builder(this, IMPORTANCE_DEFAULT_ID) 489 .setContentTitle(getString(R.string.exited_setup_title)) 490 .setContentText(getString(R.string.exited_setup_content)) 491 .setCategory(Notification.CATEGORY_CAR_INFORMATION) 492 .setSmallIcon(R.drawable.car_ic_mode) 493 .build(); 494 notificationMgr.notify(NOTIFICATION_ID, notification); 495 } 496 getSelectedDpcInfo()497 private DpcInfo getSelectedDpcInfo() { 498 return sSupportedDpcApps.get(mDpcAppsSpinner.getSelectedItemPosition()); 499 } 500 launchLegacyProvisioningWorkflow()501 private void launchLegacyProvisioningWorkflow() { 502 DpcInfo dpcInfo = getSelectedDpcInfo(); 503 if (!checkDpcAppExists(dpcInfo.packageName)) { 504 showErrorMessage("Cannot provision device because " + dpcInfo.packageName 505 + " is not available.\n Make sure it's installed for both user 0 and user " 506 + getUserId()); 507 return; 508 } 509 510 Intent intent = new Intent(); 511 intent.setComponent(dpcInfo.getLegacyActivityComponentName()); 512 Log.i(TAG, "Provisioning device using LEGACY workflow while running as user " 513 + getUserId() + ". DPC: " + dpcInfo + ". Intent: " + intent); 514 startActivityForResult(intent, REQUEST_CODE_STEP1); 515 } 516 launchProvisioningWorkflow()517 private void launchProvisioningWorkflow() { 518 DpcInfo dpcInfo = getSelectedDpcInfo(); 519 520 Intent intent = new Intent(ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE); 521 // TODO(b/170333009): add a UI with options for EXTRA_PROVISIONING_TRIGGER. 522 intent.putExtra(EXTRA_PROVISIONING_TRIGGER, PROVISIONING_TRIGGER_QR_CODE); 523 intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, 524 dpcInfo.getAdminReceiverComponentName()); 525 if (dpcInfo.checkSum != null) { 526 intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM, dpcInfo.checkSum); 527 } 528 if (dpcInfo.downloadUrl != null) { 529 intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION, 530 dpcInfo.downloadUrl); 531 } 532 533 Log.i(TAG, "Provisioning device using NEW workflow while running as user " 534 + getUserId() + ". DPC: " + dpcInfo + ". Intent: " + intent); 535 536 startActivityForResult(intent, REQUEST_CODE_STEP1); 537 } 538 disableSelfAndFinish()539 private void disableSelfAndFinish() { 540 Log.d(TAG, "disableSelfAndFinish()"); 541 542 // Remove this activity from the package manager. 543 PackageManager pm = getPackageManager(); 544 ComponentName name = new ComponentName(this, DefaultActivity.class); 545 Log.i(TAG, "Disabling itself (" + name + ") for user " + getUserId()); 546 pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 547 PackageManager.DONT_KILL_APP); 548 549 finish(); 550 } 551 552 @Override onActivityResult(int requestCode, int resultCode, Intent data)553 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 554 Log.d(TAG, "onActivityResult(): request=" + requestCode + ", result=" 555 + resultCodeToString(resultCode) + ", data=" + data); 556 557 switch (requestCode) { 558 case REQUEST_CODE_STEP1: 559 onProvisioningStep1Result(resultCode); 560 break; 561 case REQUEST_CODE_STEP2_PO: 562 case REQUEST_CODE_STEP2_DO: 563 onProvisioningStep2Result(requestCode, resultCode); 564 break; 565 default: 566 showErrorMessage("onActivityResult(): invalid request code " + requestCode); 567 568 } 569 } 570 onProvisioningStep1Result(int resultCode)571 private void onProvisioningStep1Result(int resultCode) { 572 int requestCodeStep2; 573 switch (resultCode) { 574 case RESULT_CODE_PROFILE_OWNER_SET: 575 requestCodeStep2 = REQUEST_CODE_STEP2_PO; 576 break; 577 case RESULT_CODE_DEVICE_OWNER_SET: 578 requestCodeStep2 = REQUEST_CODE_STEP2_DO; 579 break; 580 default: 581 showErrorMessage("onProvisioningStep1Result(): invalid result code " 582 + resultCodeToString(resultCode) 583 + getManagedProvisioningFailureWarning()); 584 return; 585 } 586 Intent intent = new Intent(PROVISION_FINALIZATION_INSIDE_SUW) 587 .addCategory(Intent.CATEGORY_DEFAULT); 588 Log.i(TAG, "Finalizing DPC with " + intent); 589 startActivityForResult(intent, requestCodeStep2); 590 } 591 getManagedProvisioningFailureWarning()592 private String getManagedProvisioningFailureWarning() { 593 return "\n\n" + getString(R.string.provision_failure_message); 594 } 595 onProvisioningStep2Result(int requestCode, int resultCode)596 private void onProvisioningStep2Result(int requestCode, int resultCode) { 597 boolean doMode = requestCode == REQUEST_CODE_STEP2_DO; 598 if (resultCode != RESULT_OK) { 599 StringBuilder message = new StringBuilder("onProvisioningStep2Result(): " 600 + "invalid result code ").append(resultCode); 601 if (doMode) { 602 message.append(getManagedProvisioningFailureWarning()); 603 } 604 showErrorMessage(message.toString()); 605 return; 606 } 607 608 Log.i(TAG, (doMode ? "Device owner" : "Profile owner") + " mode provisioned!"); 609 finishSetup(); 610 } 611 resultCodeToString(int resultCode)612 private static String resultCodeToString(int resultCode) { 613 StringBuilder result = new StringBuilder(); 614 switch (resultCode) { 615 case RESULT_OK: 616 result.append("RESULT_OK"); 617 break; 618 case RESULT_CANCELED: 619 result.append("RESULT_CANCELED"); 620 break; 621 case RESULT_FIRST_USER: 622 result.append("RESULT_FIRST_USER"); 623 break; 624 case RESULT_CODE_PROFILE_OWNER_SET: 625 result.append("RESULT_CODE_PROFILE_OWNER_SET"); 626 break; 627 case RESULT_CODE_DEVICE_OWNER_SET: 628 result.append("RESULT_CODE_DEVICE_OWNER_SET"); 629 break; 630 default: 631 result.append("UNKNOWN_CODE"); 632 } 633 return result.append('(').append(resultCode).append(')').toString(); 634 } 635 showErrorMessage(String message)636 private void showErrorMessage(String message) { 637 Log.e(TAG, "Error: " + message); 638 mErrorsTextView.setText(message); 639 findViewById(R.id.errors_container).setVisibility(View.VISIBLE); 640 } 641 } 642