diff options
author | Felipe Leme <felipeal@google.com> | 2020-11-09 17:24:27 -0800 |
---|---|---|
committer | Felipe Leme <felipeal@google.com> | 2020-11-12 11:55:29 -0800 |
commit | 26a72a46e82665ae56160be12125381508ecce6a (patch) | |
tree | 238f12e4ac9d54a241a23b2eed289abec4a40d8e | |
parent | eaa587d22d7e4a3628be6181a9d7c6a06735c690 (diff) | |
download | Provision-26a72a46e82665ae56160be12125381508ecce6a.tar.gz |
Fixes managed provisioning workflow:
- Don't assume it's device owne mode (could be profile as well).
- Added LocalTestDPC.
- Fixed handling of activity results.
Bug: 170333009
Bug: 172867577
Test: m -j CarProvision && adb sync &&\
adb shell am force-stop com.android.car.provision &&\
adb shell pm enable --user cur com.android.car.provision &&\
adb shell pm enable --user cur com.android.car.provision/.DefaultActivity &&\
adb shell am start -a android.intent.action.MAIN \
-c android.intent.category.HOME \
-c android.intent.category.DEFAULT \
-c android.intent.category.SETUP_WIZARD
Change-Id: I902a792825e36ee95a798f78b013ee2077971a85
-rw-r--r-- | res/layout/default_activity.xml | 10 | ||||
-rw-r--r-- | res/values/strings.xml | 8 | ||||
-rw-r--r-- | src/com/android/car/provision/DefaultActivity.java | 206 |
3 files changed, 150 insertions, 74 deletions
diff --git a/res/layout/default_activity.xml b/res/layout/default_activity.xml index b62eea6..94629dc 100644 --- a/res/layout/default_activity.xml +++ b/res/layout/default_activity.xml @@ -48,18 +48,18 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="50dp" - android:text="@string/set_do"/> + android:text="@string/provision_device"/> <Button - android:id="@+id/legacy_do_provisioning" + android:id="@+id/legacy_provision_workflow" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/legacy_do_workflow" + android:text="@string/legacy_provision_workflow" android:enabled="false"/> <Button - android:id="@+id/do_provisioning" + android:id="@+id/provision_workflow" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/do_workflow" + android:text="@string/provision_workflow" android:enabled="false"/> </LinearLayout> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" diff --git a/res/values/strings.xml b/res/values/strings.xml index 93f111d..3be5a6d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -24,13 +24,13 @@ <string name="error_prompt" translatable="false">Error:</string> <string name="finish_setup" translatable="false">Finish Setup</string> <string name="factory_reset" translatable="false">Factory Reset</string> - <string name="set_do" translatable="false">Set Device Owner</string> - <string name="legacy_do_workflow" translatable="false">Legacy</string> - <string name="do_workflow" translatable="false">S+</string> + <string name="provision_device" translatable="false">Provision device</string> + <string name="legacy_provision_workflow" translatable="false">Legacy</string> + <string name="provision_workflow" translatable="false">New workflow</string> <string name="exited_setup_title" translatable="false">Setup Exited</string> <string name="exited_setup_content" translatable="false">Don\'t worry, this is just a friendly FYI, there\'s nothing to do, you are good to go.\nHave fun!</string> - <string name="do_failure_message" translatable="false">Device owner could not be provisioned!\n + <string name="provision_failure_message" translatable="false">Device could not be provisioned!\n \nOn a production device, it should trigger a factory reset, but for development purposes, this Setup Wizard is not doing so.</string> <string name="factory_reset_warning" translatable="false">Factory Reset is irreversible, are you diff --git a/src/com/android/car/provision/DefaultActivity.java b/src/com/android/car/provision/DefaultActivity.java index b80b17e..c422695 100644 --- a/src/com/android/car/provision/DefaultActivity.java +++ b/src/com/android/car/provision/DefaultActivity.java @@ -16,6 +16,9 @@ package com.android.car.provision; +import static android.app.Activity.RESULT_CANCELED; +import static android.app.Activity.RESULT_FIRST_USER; +import static android.app.Activity.RESULT_OK; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE; import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME; import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION; @@ -47,8 +50,8 @@ import android.widget.TextView; import com.android.car.setupwizardlib.util.CarDrivingStateMonitor; -import java.util.HashMap; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; /** * Reference implementeation for a Car SetupWizard. @@ -58,7 +61,7 @@ import java.util.Map; * <ul> * <li>Shows UI where user can confirm setup. * <li>Listen to UX restriction events, so it exits setup when the car moves. - * <li>Add option to setup DeviceOwner mode. + * <li>Add option to setup manage-provision mode. * <li>Sets car-specific properties. * </ul> */ @@ -72,23 +75,47 @@ public final class DefaultActivity extends Activity { private static final String KEY_SETUP_WIZARD_IN_PROGRESS = "android.car.SETUP_WIZARD_IN_PROGRESS"; - private static final int REQUEST_CODE_SET_DO = 42; + // TODO(b/170333009): copied from ManagedProvisioning app, as they're hidden; + private static final String PROVISION_FINALIZATION_INSIDE_SUW = + "android.app.action.PROVISION_FINALIZATION_INSIDE_SUW"; + private static final int RESULT_CODE_PROFILE_OWNER_SET = 122; + private static final int RESULT_CODE_DEVICE_OWNER_SET = 123; + + + private static final int REQUEST_CODE_STEP1 = 42; + private static final int REQUEST_CODE_STEP2_PO = 43; + private static final int REQUEST_CODE_STEP2_DO = 44; private static final int NOTIFICATION_ID = 108; private static final String IMPORTANCE_DEFAULT_ID = "importance_default"; - private static final Map<String, DpcInfo> sSupportedDpcApps = new HashMap<>(1); + private static final List<DpcInfo> sSupportedDpcApps = new ArrayList<>(2); + + private static final String TEST_DPC_NAME = "TestDPC"; + private static final String TEST_DPC_PACKAGE = "com.afwsamples.testdpc"; + private static final String TEST_DPC_LEGACY_ACTIVITY = TEST_DPC_PACKAGE + + ".SetupManagementLaunchActivity"; + private static final String TEST_DPC_RECEIVER = TEST_DPC_PACKAGE + + ".DeviceAdminReceiver"; + private static final String LOCAL_TEST_DPC_NAME = "LocalTestDPC"; static { // TODO(b/170333009): add a UI with multiple options once AAOS provides a CarTestDPC app. - DpcInfo testDpc = new DpcInfo("TestDPC", - "com.afwsamples.testdpc", - "com.afwsamples.testdpc.SetupManagementLaunchActivity", - "com.afwsamples.testdpc.DeviceAdminReceiver", - // TODO(b/170333009): add UI to set checkSum for local built app + DpcInfo testDpc = new DpcInfo(TEST_DPC_NAME, + TEST_DPC_PACKAGE, + TEST_DPC_LEGACY_ACTIVITY, + TEST_DPC_RECEIVER, "gJD2YwtOiWJHkSMkkIfLRlj-quNqG1fb6v100QmzM9w=", "https://testdpc-latest-apk.appspot.com/preview"); - sSupportedDpcApps.put(testDpc.name, testDpc); + // Locally-built version of the TestDPC + DpcInfo localTestDpc = new DpcInfo(LOCAL_TEST_DPC_NAME, + TEST_DPC_PACKAGE, + TEST_DPC_LEGACY_ACTIVITY, + TEST_DPC_RECEIVER, + /* checkSum= */ null, + /* downloadUrl = */ null); + sSupportedDpcApps.add(localTestDpc); + sSupportedDpcApps.add(testDpc); } private CarDrivingStateMonitor mCarDrivingStateMonitor; @@ -96,8 +123,8 @@ public final class DefaultActivity extends Activity { private TextView mErrorsTextView; private Button mFinishSetupButton; private Button mFactoryResetButton; - private Button mDoProvisioningLegacyWorkflowButton; - private Button mDoProvisioningButton; + private Button mLegacyProvisioningWorkflowButton; + private Button mProvisioningWorkflowButton; private final BroadcastReceiver mDrivingStateExitReceiver = new BroadcastReceiver() { @Override @@ -125,13 +152,16 @@ public final class DefaultActivity extends Activity { mErrorsTextView = findViewById(R.id.error_message); mFinishSetupButton = findViewById(R.id.finish_setup); mFactoryResetButton = findViewById(R.id.factory_reset); - mDoProvisioningLegacyWorkflowButton = findViewById(R.id.legacy_do_provisioning); - mDoProvisioningButton = findViewById(R.id.do_provisioning); + mLegacyProvisioningWorkflowButton = findViewById(R.id.legacy_provision_workflow); + mProvisioningWorkflowButton = findViewById(R.id.provision_workflow); + mLegacyProvisioningWorkflowButton + .setOnClickListener((v) -> launchLegacyProvisioningWorkflow()); + mProvisioningWorkflowButton.setOnClickListener((v) -> launchProvisioningWorkflow()); mFinishSetupButton.setOnClickListener((v) -> finishSetup()); mFactoryResetButton.setOnClickListener((v) -> factoryReset()); - setDoProvisioning(); + setMananagedProvisioning(); startMonitor(); } @@ -162,25 +192,22 @@ public final class DefaultActivity extends Activity { } } - private void setDoProvisioning() { + private void setMananagedProvisioning() { if (!getPackageManager() .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) { - Log.i(TAG, "Disabling DeviceOwner buttom because device does not have the " + Log.i(TAG, "Disabling provisioning buttons because device does not have the " + PackageManager.FEATURE_DEVICE_ADMIN + " feature"); return; } DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class); if (!dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE)) { - Log.w(TAG, "Disabling DeviceOwner buttom because it cannot be provisioned - it can only" - + " be set on first boot"); + Log.w(TAG, "Disabling provisioning buttons because device cannot be provisioned - " + + "it can only be set on first boot"); return; } - mDoProvisioningButton.setEnabled(true); - mDoProvisioningButton.setOnClickListener((v) -> provisionDeviceOwner()); - mDoProvisioningLegacyWorkflowButton.setEnabled(true); - mDoProvisioningLegacyWorkflowButton - .setOnClickListener((v) -> provisionDeviceOwnerLegacyWorkflow()); + mProvisioningWorkflowButton.setEnabled(true); + mLegacyProvisioningWorkflowButton.setEnabled(true); } private boolean checkDpcAppExists(String dpcApp) { @@ -270,11 +297,15 @@ public final class DefaultActivity extends Activity { notificationMgr.notify(NOTIFICATION_ID, notification); } - private void provisionDeviceOwnerLegacyWorkflow() { + private DpcInfo getSelectedDpcInfo() { // TODO(b/170333009): add a UI with multiple options once AAOS provides a CarTestDPC app. - DpcInfo dpcInfo = sSupportedDpcApps.values().iterator().next(); + return sSupportedDpcApps.get(0); + } + + private void launchLegacyProvisioningWorkflow() { + DpcInfo dpcInfo = getSelectedDpcInfo(); if (!checkDpcAppExists(dpcInfo.packageName)) { - showErrorMessage("Cannot setup DeviceOwner because " + dpcInfo.packageName + showErrorMessage("Cannot provision device because " + dpcInfo.packageName + " is not available.\n Make sure it's installed for both user 0 and user " + getUserId()); return; @@ -282,28 +313,31 @@ public final class DefaultActivity extends Activity { Intent intent = new Intent(); intent.setComponent(dpcInfo.getLegacyActivityComponentName()); - Log.i(TAG, "Provisioning device owner using LEGACY workflow while running as user " + Log.i(TAG, "Provisioning device using LEGACY workflow while running as user " + getUserId() + ". DPC: " + dpcInfo + ". Intent: " + intent); - startActivityForResult(intent, REQUEST_CODE_SET_DO); + startActivityForResult(intent, REQUEST_CODE_STEP1); } - private void provisionDeviceOwner() { - // TODO(b/170333009): add a UI with multiple options once AAOS provides a CarTestDPC app. - DpcInfo dpcInfo = sSupportedDpcApps.values().iterator().next(); + private void launchProvisioningWorkflow() { + DpcInfo dpcInfo = getSelectedDpcInfo(); Intent intent = new Intent(ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE); // TODO(b/170333009): add a UI with options for EXTRA_PROVISIONING_TRIGGER. intent.putExtra(EXTRA_PROVISIONING_TRIGGER, PROVISIONING_TRIGGER_QR_CODE); intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, dpcInfo.getAdminReceiverComponentName()); - intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM, dpcInfo.checkSum); - intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION, - dpcInfo.downloadUrl); + if (dpcInfo.checkSum != null) { + intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM, dpcInfo.checkSum); + } + if (dpcInfo.downloadUrl != null) { + intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION, + dpcInfo.downloadUrl); + } - Log.i(TAG, "Provisioning device owner using NEW workflow while running as user " + Log.i(TAG, "Provisioning device using NEW workflow while running as user " + getUserId() + ". DPC: " + dpcInfo + ". Intent: " + intent); - startActivityForResult(intent, REQUEST_CODE_SET_DO); + startActivityForResult(intent, REQUEST_CODE_STEP1); } private void disableSelfAndFinish() { @@ -319,44 +353,86 @@ public final class DefaultActivity extends Activity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - Log.d(TAG, "onActivityResult(): request=" + requestCode + ", result=" + resultCode - + ", data=" + data); - StringBuilder error = new StringBuilder(); - if (requestCode != REQUEST_CODE_SET_DO) { - error.append("onActivityResult(): got invalid request code ").append(requestCode); - } else if (resultCode != Activity.RESULT_OK) { - error.append("onActivityResult(): got invalid result code ").append(resultCode); - } - if (error.length() > 0) { - error.append('\n').append(getString(R.string.do_failure_message)); - showErrorMessage(error.toString()); - return; - } + Log.d(TAG, "onActivityResult(): request=" + requestCode + ", result=" + + resultCodeToString(resultCode) + ", data=" + data); + + switch (requestCode) { + case REQUEST_CODE_STEP1: + onProvisioningStep1Result(resultCode); + break; + case REQUEST_CODE_STEP2_PO: + case REQUEST_CODE_STEP2_DO: + onProvisioningStep2Result(requestCode, resultCode); + break; + default: + showErrorMessage("onActivityResult(): invalid request code " + requestCode); - Log.i(TAG, "Device owner mode provisioned!"); - finishSetup(); - finalizeDpc(); + } } - private void finalizeDpc() { - // TODO(b/170333009): use proper constant for intent action - Intent intent = new Intent("android.app.action.PROVISION_FINALIZATION_INSIDE_SUW") + private void onProvisioningStep1Result(int resultCode) { + int requestCodeStep2; + switch (resultCode) { + case RESULT_CODE_PROFILE_OWNER_SET: + requestCodeStep2 = REQUEST_CODE_STEP2_PO; + break; + case RESULT_CODE_DEVICE_OWNER_SET: + requestCodeStep2 = REQUEST_CODE_STEP2_DO; + break; + default: + showErrorMessage("onProvisioningStep1Result(): invalid result code " + + resultCodeToString(resultCode) + + getManagedProvisioningFailureWarning()); + return; + } + Intent intent = new Intent(PROVISION_FINALIZATION_INSIDE_SUW) .addCategory(Intent.CATEGORY_DEFAULT); Log.i(TAG, "Finalizing DPC with " + intent); - startActivity(intent); + startActivityForResult(intent, requestCodeStep2); + } + + private String getManagedProvisioningFailureWarning() { + return "\n\n" + getString(R.string.provision_failure_message); + } + + private void onProvisioningStep2Result(int requestCode, int resultCode) { + boolean doMode = requestCode == REQUEST_CODE_STEP2_DO; + if (resultCode != RESULT_OK) { + StringBuilder message = new StringBuilder("onProvisioningStep2Result(): " + + "invalid result code ").append(resultCode); + if (doMode) { + message.append(getManagedProvisioningFailureWarning()); + } + showErrorMessage(message.toString()); + return; + } + + Log.i(TAG, (doMode ? "Device owner" : "Profile owner") + " mode provisioned!"); + finishSetup(); } private static String resultCodeToString(int resultCode) { + StringBuilder result = new StringBuilder(); switch (resultCode) { - case Activity.RESULT_OK: - return "RESULT_OK"; - case Activity.RESULT_CANCELED: - return "RESULT_CANCELED"; - case Activity.RESULT_FIRST_USER: - return "RESULT_FIRST_USER"; + case RESULT_OK: + result.append("RESULT_OK"); + break; + case RESULT_CANCELED: + result.append("RESULT_CANCELED"); + break; + case RESULT_FIRST_USER: + result.append("RESULT_FIRST_USER"); + break; + case RESULT_CODE_PROFILE_OWNER_SET: + result.append("RESULT_CODE_PROFILE_OWNER_SET"); + break; + case RESULT_CODE_DEVICE_OWNER_SET: + result.append("RESULT_CODE_DEVICE_OWNER_SET"); + break; default: - return "UNKNOWN_CODE_" + resultCode; + result.append("UNKNOWN_CODE"); } + return result.append('(').append(resultCode).append(')').toString(); } private void showErrorMessage(String message) { |