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