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 Context.RECEIVER_EXPORTED); 224 225 mCarDrivingStateMonitor = CarDrivingStateMonitor.get(this); 226 mCarDrivingStateMonitor.startMonitor(); 227 } 228 229 @Override finish()230 public void finish() { 231 Log.i(TAG, "finish() for user " + getUserId()); 232 233 stopMonitor(); 234 235 super.finish(); 236 }; 237 238 @Override dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args)239 public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) { 240 if (args == null || args.length == 0) { 241 showDpcs(pw); 242 showHelp(pw); 243 return; 244 } 245 246 if (args[0].equals("--help")) { 247 showHelp(pw); 248 return; 249 } 250 251 addDpc(pw, args); 252 }; 253 showDpcs(PrintWriter pw)254 private void showDpcs(PrintWriter pw) { 255 pw.printf("%d DPCs\n", sSupportedDpcApps.size()); 256 sSupportedDpcApps.forEach((dpc) -> pw.printf("\t%s\n", dpc)); 257 } 258 showHelp(PrintWriter pw)259 private void showHelp(PrintWriter pw) { 260 pw.println("\nTo add a new DPC, use: --name name --package-name package-name" 261 + "--receiver-name receiver-name [--legacy-activity-name legacy-activity-name] " 262 + "[--checksum checksum] [--download-url download-url]"); 263 } 264 addDpc(PrintWriter pw, String[] args)265 private void addDpc(PrintWriter pw, String[] args) { 266 String name = null; 267 String packageName = null; 268 String legacyActivityName = null; 269 String receiverName = null; 270 String checkSum = null; 271 String downloadUrl = null; 272 273 for (int i = 0; i < args.length; i++) { 274 try { 275 switch (args[i]) { 276 case "--name": 277 name = args[++i]; 278 break; 279 case "--package-name": 280 packageName = args[++i]; 281 break; 282 case "--legacy-activity-name": 283 legacyActivityName = args[++i]; 284 break; 285 case "--receiver-name": 286 receiverName = args[++i]; 287 break; 288 case "--checksum": 289 checkSum = args[++i]; 290 break; 291 case "--download-url": 292 downloadUrl = args[++i]; 293 break; 294 default: 295 pw.printf("Invalid option at index %d: %s\n", i, args[i]); 296 return; 297 } 298 } catch (Exception e) { 299 // most likely a missing arg... 300 pw.printf("Error handing arg %d: %s\n", i, e); 301 return; 302 } 303 } 304 305 DpcInfo dpc = new DpcInfo(name, packageName, legacyActivityName, receiverName, checkSum, 306 downloadUrl); 307 Log.i(TAG, "Adding new DPC from dump(): " + dpc); 308 sSupportedDpcApps.add(dpc); 309 pw.printf("Added new DPC: %s\n", dpc); 310 311 updateUi(); 312 } 313 stopMonitor()314 private void stopMonitor() { 315 Log.d(TAG, "stopMonitor()"); 316 317 if (mCarDrivingStateMonitor == null) { 318 // Happens when device is managed (and startMonitor() is skipped) 319 Log.d(TAG, "Already stopped (or never stopped)"); 320 return; 321 } 322 323 if (mDrivingStateExitReceiver != null) { 324 unregisterReceiver(mDrivingStateExitReceiver); 325 } 326 327 mCarDrivingStateMonitor.stopMonitor(); 328 mCarDrivingStateMonitor = null; 329 } 330 hideSystemUi()331 private void hideSystemUi() { 332 WindowInsetsController insetsController = getWindow().getDecorView() 333 .getWindowInsetsController(); 334 if (insetsController == null) { 335 Log.w(TAG, "No insets controller"); 336 return; 337 } 338 Log.d(TAG, "Hiding the system UI bars"); 339 insetsController.hide(WindowInsets.Type.navigationBars()); 340 } 341 updateUi()342 private void updateUi() { 343 String[] appNames = new String[sSupportedDpcApps.size()]; 344 for (int i = 0; i < sSupportedDpcApps.size(); i++) { 345 appNames[i] = sSupportedDpcApps.get(i).name; 346 } 347 mDpcAppsSpinner.setAdapter(new ArrayAdapter<String>(this, 348 android.R.layout.simple_spinner_item, appNames)); 349 mDpcAppsSpinner.setSelection(appNames.length - 1); 350 } 351 setManagedProvisioning(DevicePolicyManager dpm)352 private void setManagedProvisioning(DevicePolicyManager dpm) { 353 if (!getPackageManager() 354 .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) { 355 Log.i(TAG, "Disabling provisioning buttons because device does not have the " 356 + PackageManager.FEATURE_DEVICE_ADMIN + " feature"); 357 return; 358 } 359 if (!dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE)) { 360 Log.w(TAG, "Disabling provisioning buttons because device cannot be provisioned - " 361 + "it can only be set on first boot"); 362 return; 363 } 364 365 mProvisioningWorkflowButton.setEnabled(true); 366 mLegacyProvisioningWorkflowButton.setEnabled(true); 367 } 368 checkDpcAppExists(String dpcApp)369 private boolean checkDpcAppExists(String dpcApp) { 370 if (!checkAppExists(dpcApp, UserHandle.USER_SYSTEM)) return false; 371 if (!checkAppExists(dpcApp, getUserId())) return false; 372 return true; 373 } 374 checkAppExists(String app, int userId)375 private boolean checkAppExists(String app, int userId) { 376 Log.d(TAG, "Checking if " + app + " exits for user " + userId); 377 try { 378 PackageInfo info = getPackageManager().getPackageInfoAsUser(app, /* flags= */ 0, 379 userId); 380 if (info == null) { 381 Log.i(TAG, "No app " + app + " for user " + userId); 382 return false; 383 } 384 Log.d(TAG, "Found it: " + info); 385 return true; 386 } catch (PackageManager.NameNotFoundException e) { 387 return false; 388 } catch (Exception e) { 389 Log.e(TAG, "Error checking if " + app + " exists for user " + userId, e); 390 return false; 391 } 392 } 393 finishSetup()394 private void finishSetup() { 395 Log.i(TAG, "finishing setup for user " + getUserId()); 396 provisionUserAndDevice(); 397 disableSelfAndFinish(); 398 } 399 factoryReset()400 private void factoryReset() { 401 new AlertDialog.Builder(this).setMessage(R.string.factory_reset_warning) 402 .setPositiveButton(android.R.string.ok, (d, w)->sendFactoryResetIntent()) 403 .show(); 404 } 405 sendFactoryResetIntent()406 private void sendFactoryResetIntent() { 407 provisionUserAndDevice(); 408 409 Intent intent = new Intent(Intent.ACTION_FACTORY_RESET); 410 intent.setPackage("android"); 411 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 412 intent.putExtra(Intent.EXTRA_REASON, "Requested by user on SUW"); 413 414 Log.i(TAG, "factory resetting device with intent " + intent); 415 sendBroadcast(intent); 416 417 disableSelfAndFinish(); 418 } 419 provisionUserAndDevice()420 private void provisionUserAndDevice() { 421 Log.d(TAG, "setting Settings properties"); 422 // Add a persistent setting to allow other apps to know the device has been provisioned. 423 if (!isDeviceProvisioned()) { 424 Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1); 425 } 426 427 maybeMarkSystemUserSetupComplete(); 428 Log.v(TAG, "Marking USER_SETUP_COMPLETE for user " + getUserId()); 429 markUserSetupComplete(this); 430 431 // Set car-specific properties 432 setCarSetupInProgress(false); 433 Settings.Secure.putInt(getContentResolver(), KEY_ENABLE_INITIAL_NOTICE_SCREEN_TO_USER, 0); 434 } 435 isDeviceProvisioned()436 private boolean isDeviceProvisioned() { 437 try { 438 return Settings.Global.getInt(getContentResolver(), 439 Settings.Global.DEVICE_PROVISIONED) == 1; 440 } catch (SettingNotFoundException e) { 441 Log.wtf(TAG, "DEVICE_PROVISIONED is not found."); 442 return false; 443 } 444 } 445 isUserSetupComplete(Context context)446 private boolean isUserSetupComplete(Context context) { 447 return Settings.Secure.getInt(context.getContentResolver(), 448 Settings.Secure.USER_SETUP_COMPLETE, /* default= */ 0) == 1; 449 } 450 maybeMarkSystemUserSetupComplete()451 private void maybeMarkSystemUserSetupComplete() { 452 Context systemUserContext = getApplicationContext().createContextAsUser( 453 UserHandle.SYSTEM, /* flags= */ 0); 454 if (!isUserSetupComplete(systemUserContext) && getUserId() != UserHandle.USER_SYSTEM 455 && UserManager.isHeadlessSystemUserMode()) { 456 Log.v(TAG, "Marking USER_SETUP_COMPLETE for system user"); 457 markUserSetupComplete(systemUserContext); 458 } 459 } 460 setCarSetupInProgress(boolean inProgress)461 private void setCarSetupInProgress(boolean inProgress) { 462 Settings.Secure.putInt(getContentResolver(), KEY_SETUP_WIZARD_IN_PROGRESS, 463 inProgress ? 1 : 0); 464 } 465 markUserSetupComplete(Context context)466 private void markUserSetupComplete(Context context) { 467 Settings.Secure.putInt(context.getContentResolver(), 468 Settings.Secure.USER_SETUP_COMPLETE, 1); 469 } 470 exitSetup()471 private void exitSetup() { 472 Log.d(TAG, "exiting setup early for user " + getUserId()); 473 provisionUserAndDevice(); 474 notifySetupExited(); 475 disableSelfAndFinish(); 476 } 477 notifySetupExited()478 private void notifySetupExited() { 479 Log.d(TAG, "Sending exited setup notification"); 480 481 NotificationManager notificationMgr = getSystemService(NotificationManager.class); 482 notificationMgr.createNotificationChannel(new NotificationChannel( 483 IMPORTANCE_DEFAULT_ID, "Importance Default", 484 NotificationManager.IMPORTANCE_DEFAULT)); 485 Notification notification = new Notification 486 .Builder(this, IMPORTANCE_DEFAULT_ID) 487 .setContentTitle(getString(R.string.exited_setup_title)) 488 .setContentText(getString(R.string.exited_setup_content)) 489 .setCategory(Notification.CATEGORY_CAR_INFORMATION) 490 .setSmallIcon(R.drawable.car_ic_mode) 491 .build(); 492 notificationMgr.notify(NOTIFICATION_ID, notification); 493 } 494 getSelectedDpcInfo()495 private DpcInfo getSelectedDpcInfo() { 496 return sSupportedDpcApps.get(mDpcAppsSpinner.getSelectedItemPosition()); 497 } 498 launchLegacyProvisioningWorkflow()499 private void launchLegacyProvisioningWorkflow() { 500 DpcInfo dpcInfo = getSelectedDpcInfo(); 501 if (!checkDpcAppExists(dpcInfo.packageName)) { 502 showErrorMessage("Cannot provision device because " + dpcInfo.packageName 503 + " is not available.\n Make sure it's installed for both user 0 and user " 504 + getUserId()); 505 return; 506 } 507 508 Intent intent = new Intent(); 509 intent.setComponent(dpcInfo.getLegacyActivityComponentName()); 510 Log.i(TAG, "Provisioning device using LEGACY workflow while running as user " 511 + getUserId() + ". DPC: " + dpcInfo + ". Intent: " + intent); 512 startActivityForResult(intent, REQUEST_CODE_STEP1); 513 } 514 launchProvisioningWorkflow()515 private void launchProvisioningWorkflow() { 516 DpcInfo dpcInfo = getSelectedDpcInfo(); 517 518 Intent intent = new Intent(ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE); 519 // TODO(b/170333009): add a UI with options for EXTRA_PROVISIONING_TRIGGER. 520 intent.putExtra(EXTRA_PROVISIONING_TRIGGER, PROVISIONING_TRIGGER_QR_CODE); 521 intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, 522 dpcInfo.getAdminReceiverComponentName()); 523 if (dpcInfo.checkSum != null) { 524 intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM, dpcInfo.checkSum); 525 } 526 if (dpcInfo.downloadUrl != null) { 527 intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION, 528 dpcInfo.downloadUrl); 529 } 530 531 Log.i(TAG, "Provisioning device using NEW workflow while running as user " 532 + getUserId() + ". DPC: " + dpcInfo + ". Intent: " + intent); 533 534 startActivityForResult(intent, REQUEST_CODE_STEP1); 535 } 536 disableSelfAndFinish()537 private void disableSelfAndFinish() { 538 Log.d(TAG, "disableSelfAndFinish()"); 539 540 // Remove this activity from the package manager. 541 PackageManager pm = getPackageManager(); 542 ComponentName name = new ComponentName(this, DefaultActivity.class); 543 Log.i(TAG, "Disabling itself (" + name + ") for user " + getUserId()); 544 pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 545 PackageManager.DONT_KILL_APP); 546 547 finish(); 548 } 549 550 @Override onActivityResult(int requestCode, int resultCode, Intent data)551 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 552 Log.d(TAG, "onActivityResult(): request=" + requestCode + ", result=" 553 + resultCodeToString(resultCode) + ", data=" + data); 554 555 switch (requestCode) { 556 case REQUEST_CODE_STEP1: 557 onProvisioningStep1Result(resultCode); 558 break; 559 case REQUEST_CODE_STEP2_PO: 560 case REQUEST_CODE_STEP2_DO: 561 onProvisioningStep2Result(requestCode, resultCode); 562 break; 563 default: 564 showErrorMessage("onActivityResult(): invalid request code " + requestCode); 565 566 } 567 } 568 onProvisioningStep1Result(int resultCode)569 private void onProvisioningStep1Result(int resultCode) { 570 int requestCodeStep2; 571 switch (resultCode) { 572 case RESULT_CODE_PROFILE_OWNER_SET: 573 requestCodeStep2 = REQUEST_CODE_STEP2_PO; 574 break; 575 case RESULT_CODE_DEVICE_OWNER_SET: 576 requestCodeStep2 = REQUEST_CODE_STEP2_DO; 577 break; 578 default: 579 showErrorMessage("onProvisioningStep1Result(): invalid result code " 580 + resultCodeToString(resultCode) 581 + getManagedProvisioningFailureWarning()); 582 return; 583 } 584 Intent intent = new Intent(PROVISION_FINALIZATION_INSIDE_SUW) 585 .addCategory(Intent.CATEGORY_DEFAULT); 586 Log.i(TAG, "Finalizing DPC with " + intent); 587 startActivityForResult(intent, requestCodeStep2); 588 } 589 getManagedProvisioningFailureWarning()590 private String getManagedProvisioningFailureWarning() { 591 return "\n\n" + getString(R.string.provision_failure_message); 592 } 593 onProvisioningStep2Result(int requestCode, int resultCode)594 private void onProvisioningStep2Result(int requestCode, int resultCode) { 595 boolean doMode = requestCode == REQUEST_CODE_STEP2_DO; 596 if (resultCode != RESULT_OK) { 597 StringBuilder message = new StringBuilder("onProvisioningStep2Result(): " 598 + "invalid result code ").append(resultCode); 599 if (doMode) { 600 message.append(getManagedProvisioningFailureWarning()); 601 } 602 showErrorMessage(message.toString()); 603 return; 604 } 605 606 Log.i(TAG, (doMode ? "Device owner" : "Profile owner") + " mode provisioned!"); 607 finishSetup(); 608 } 609 resultCodeToString(int resultCode)610 private static String resultCodeToString(int resultCode) { 611 StringBuilder result = new StringBuilder(); 612 switch (resultCode) { 613 case RESULT_OK: 614 result.append("RESULT_OK"); 615 break; 616 case RESULT_CANCELED: 617 result.append("RESULT_CANCELED"); 618 break; 619 case RESULT_FIRST_USER: 620 result.append("RESULT_FIRST_USER"); 621 break; 622 case RESULT_CODE_PROFILE_OWNER_SET: 623 result.append("RESULT_CODE_PROFILE_OWNER_SET"); 624 break; 625 case RESULT_CODE_DEVICE_OWNER_SET: 626 result.append("RESULT_CODE_DEVICE_OWNER_SET"); 627 break; 628 default: 629 result.append("UNKNOWN_CODE"); 630 } 631 return result.append('(').append(resultCode).append(')').toString(); 632 } 633 showErrorMessage(String message)634 private void showErrorMessage(String message) { 635 Log.e(TAG, "Error: " + message); 636 mErrorsTextView.setText(message); 637 findViewById(R.id.errors_container).setVisibility(View.VISIBLE); 638 } 639 } 640