summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelipe Leme <felipeal@google.com>2020-11-09 17:24:27 -0800
committerFelipe Leme <felipeal@google.com>2020-11-12 11:55:29 -0800
commit26a72a46e82665ae56160be12125381508ecce6a (patch)
tree238f12e4ac9d54a241a23b2eed289abec4a40d8e
parenteaa587d22d7e4a3628be6181a9d7c6a06735c690 (diff)
downloadProvision-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.xml10
-rw-r--r--res/values/strings.xml8
-rw-r--r--src/com/android/car/provision/DefaultActivity.java206
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) {