• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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