blob: 92720e3212455d4203295289f490b431a0e138a9 [file] [log] [blame]
Jean-Baptiste Queruc4a805b2009-09-28 14:03:57 -07001/*
2 * Copyright (C) 2008 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
17package com.android.provision;
18
Felipe Leme23356222020-11-17 16:11:03 -080019import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
20import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
21import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION;
22import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM;
23import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TRIGGER;
24
25import static com.android.provision.Utils.DEFAULT_SETTINGS_PROVISION_DO_MODE;
26import static com.android.provision.Utils.SETTINGS_PROVISION_DO_MODE;
27import static com.android.provision.Utils.TAG;
28import static com.android.provision.Utils.getSettings;
29
Jean-Baptiste Queruc4a805b2009-09-28 14:03:57 -070030import android.app.Activity;
Felipe Leme23356222020-11-17 16:11:03 -080031import android.app.AlertDialog;
32import android.app.admin.DevicePolicyManager;
Jean-Baptiste Queruc4a805b2009-09-28 14:03:57 -070033import android.content.ComponentName;
Felipe Leme23356222020-11-17 16:11:03 -080034import android.content.Intent;
Jean-Baptiste Queruc4a805b2009-09-28 14:03:57 -070035import android.content.pm.PackageManager;
36import android.os.Bundle;
37import android.provider.Settings;
Felipe Leme23356222020-11-17 16:11:03 -080038import android.util.Log;
Jean-Baptiste Queruc4a805b2009-09-28 14:03:57 -070039
40/**
Felipe Leme23356222020-11-17 16:11:03 -080041 * Application that sets the provisioned bit, like {@code SetupWizard} does.
42 *
43 * <p>By default, it silently provisions the device, but it can also be used to provision
44 * {@code DeviceOwner}. For example, to set the {@code TestDPC} app, run the steps below:
45 * <pre><code>
46 adb root
47 adb install PATH_TO_TESTDPC_APK
48 adb shell settings put secure tmp_provision_set_do 1
49 adb shell settings put secure tmp_provision_package com.afwsamples.testdpc
50 adb shell settings put secure tmp_provision_receiver com.afwsamples.testdpc.DeviceAdminReceiver
51 adb shell settings put secure tmp_provision_trigger 2
52 adb shell rm /data/system/device_policies.xml
53 adb shell settings put global device_provisioned 0
54 adb shell settings put secure user_setup_complete 0
55 adb shell pm enable com.android.provision
56 adb shell pm enable com.android.provision/.DefaultActivity
57 adb shell stop && adb shell start
58
59 // You might also need to run:
60 adb shell am start com.android.provision/.DefaultActivity
61
62 * </code></pre>
Jean-Baptiste Queruc4a805b2009-09-28 14:03:57 -070063 */
64public class DefaultActivity extends Activity {
65
Felipe Leme23356222020-11-17 16:11:03 -080066 // TODO(b/170333009): copied from ManagedProvisioning app, as they're hidden;
67 private static final String PROVISION_FINALIZATION_INSIDE_SUW =
68 "android.app.action.PROVISION_FINALIZATION_INSIDE_SUW";
69 private static final int RESULT_CODE_PROFILE_OWNER_SET = 122;
70 private static final int RESULT_CODE_DEVICE_OWNER_SET = 123;
71
72 private static final int REQUEST_CODE_STEP1 = 42;
73 private static final int REQUEST_CODE_STEP2_PO = 43;
74 private static final int REQUEST_CODE_STEP2_DO = 44;
75
Jean-Baptiste Queruc4a805b2009-09-28 14:03:57 -070076 @Override
77 protected void onCreate(Bundle icicle) {
78 super.onCreate(icicle);
79
Felipe Leme23356222020-11-17 16:11:03 -080080 boolean provisionDeviceOwner = getSettings(getContentResolver(), SETTINGS_PROVISION_DO_MODE,
81 DEFAULT_SETTINGS_PROVISION_DO_MODE) == 1;
82
83 if (provisionDeviceOwner) {
84 provisionDeviceOwner();
85 return;
86 }
87 finishSetup();
88 }
89
90 private void finishSetup() {
91 setProvisioningState();
92 disableSelfAndFinish();
93 }
94
95 private void setProvisioningState() {
96 Log.i(TAG, "Setting provisioning state");
Jean-Baptiste Queruc4a805b2009-09-28 14:03:57 -070097 // Add a persistent setting to allow other apps to know the device has been provisioned.
Jeff Brown10c30032012-09-25 15:00:37 -070098 Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
Jean-Baptiste Queru7bc55872013-03-12 07:26:37 -070099 Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);
Felipe Leme23356222020-11-17 16:11:03 -0800100 }
Jean-Baptiste Queruc4a805b2009-09-28 14:03:57 -0700101
Felipe Leme23356222020-11-17 16:11:03 -0800102 private void disableSelfAndFinish() {
Jean-Baptiste Queruc4a805b2009-09-28 14:03:57 -0700103 // remove this activity from the package manager.
104 PackageManager pm = getPackageManager();
105 ComponentName name = new ComponentName(this, DefaultActivity.class);
Felipe Leme23356222020-11-17 16:11:03 -0800106 Log.i(TAG, "Disabling itself (" + name + ")");
Dianne Hackborn86bf6d82011-10-19 15:42:36 -0700107 pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
108 PackageManager.DONT_KILL_APP);
Jean-Baptiste Queruc4a805b2009-09-28 14:03:57 -0700109 // terminate the activity.
110 finish();
111 }
Jean-Baptiste Queruc4a805b2009-09-28 14:03:57 -0700112
Felipe Leme23356222020-11-17 16:11:03 -0800113 private void provisionDeviceOwner() {
114 if (!getPackageManager()
115 .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
116 Log.e(TAG, "Cannot set up device owner because device does not have the "
117 + PackageManager.FEATURE_DEVICE_ADMIN + " feature");
118 finishSetup();
119 return;
120 }
121 DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
122 if (!dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE)) {
123 Log.e(TAG, "DeviceOwner provisioning is not allowed, most like device is already "
124 + "provisioned");
125 finishSetup();
126 return;
127 }
128
129 DpcInfo dpcInfo = new DpcInfo(getContentResolver());
130 Intent intent = new Intent(ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE);
131 intent.putExtra(EXTRA_PROVISIONING_TRIGGER, dpcInfo.trigger);
132 intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
133 dpcInfo.getReceiverComponentName());
134 if (dpcInfo.checkSum != null) {
135 intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM, dpcInfo.checkSum);
136 }
137 if (dpcInfo.downloadUrl != null) {
138 intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION,
139 dpcInfo.downloadUrl);
140 }
141
142 Log.i(TAG, "Provisioning device with " + dpcInfo + ". Intent: " + intent);
143 startActivityForResult(intent, REQUEST_CODE_STEP1);
144 }
145
146 @Override
147 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
148 Log.d(TAG, "onActivityResult(): request=" + requestCode + ", result="
149 + resultCodeToString(resultCode) + ", data=" + data);
150
151 switch (requestCode) {
152 case REQUEST_CODE_STEP1:
153 onProvisioningStep1Result(resultCode);
154 break;
155 case REQUEST_CODE_STEP2_PO:
156 case REQUEST_CODE_STEP2_DO:
157 onProvisioningStep2Result(requestCode, resultCode);
158 break;
159 default:
160 showErrorMessage("onActivityResult(): invalid request code " + requestCode);
161 }
162 }
163
164 private void onProvisioningStep1Result(int resultCode) {
165 int requestCodeStep2;
166 switch (resultCode) {
167 case RESULT_CODE_PROFILE_OWNER_SET:
168 requestCodeStep2 = REQUEST_CODE_STEP2_PO;
169 break;
170 case RESULT_CODE_DEVICE_OWNER_SET:
171 requestCodeStep2 = REQUEST_CODE_STEP2_DO;
172 break;
173 default:
174 factoryReset("invalid response from "
175 + ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE + ": "
176 + resultCodeToString(resultCode));
177 return;
178 }
179 Intent intent = new Intent(PROVISION_FINALIZATION_INSIDE_SUW)
180 .addCategory(Intent.CATEGORY_DEFAULT);
181 Log.i(TAG, "Finalizing DPC with " + intent);
182 startActivityForResult(intent, requestCodeStep2);
183 }
184
185 private void onProvisioningStep2Result(int requestCode, int resultCode) {
186 // Must set state before launching the intent that finalize the DPC, because the DPC
187 // implementation might not remove the back button
188 setProvisioningState();
189
190 boolean doMode = requestCode == REQUEST_CODE_STEP2_DO;
191 if (resultCode != RESULT_OK) {
192 factoryReset("invalid response from " + PROVISION_FINALIZATION_INSIDE_SUW + ": "
193 + resultCodeToString(resultCode));
194 return;
195 }
196
197 Log.i(TAG, (doMode ? "Device owner" : "Profile owner") + " mode provisioned!");
198 disableSelfAndFinish();
199 }
200
201 private static String resultCodeToString(int resultCode) {
202 StringBuilder result = new StringBuilder();
203 switch (resultCode) {
204 case RESULT_OK:
205 result.append("RESULT_OK");
206 break;
207 case RESULT_CANCELED:
208 result.append("RESULT_CANCELED");
209 break;
210 case RESULT_FIRST_USER:
211 result.append("RESULT_FIRST_USER");
212 break;
213 case RESULT_CODE_PROFILE_OWNER_SET:
214 result.append("RESULT_CODE_PROFILE_OWNER_SET");
215 break;
216 case RESULT_CODE_DEVICE_OWNER_SET:
217 result.append("RESULT_CODE_DEVICE_OWNER_SET");
218 break;
219 default:
220 result.append("UNKNOWN_CODE");
221 }
222 return result.append('(').append(resultCode).append(')').toString();
223 }
224
225 private void showErrorMessage(String message) {
226 Log.e(TAG, "Error: " + message);
227 }
228
229 private void factoryReset(String reason) {
230 new AlertDialog.Builder(this)
231 .setMessage("Device owner provisioning failed (" + reason
232 + ") and device must be factory reset")
233 .setPositiveButton("Reset", (d, w) -> sendFactoryResetIntent(reason))
234 .setOnDismissListener((d) -> sendFactoryResetIntent(reason))
235 .show();
236 }
237
238 private void sendFactoryResetIntent(String reason) {
239 Log.e(TAG, "Factory resetting: " + reason);
240 Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
241 intent.setPackage("android");
242 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
243 intent.putExtra(Intent.EXTRA_REASON, reason);
244
245 sendBroadcast(intent);
246
247 // Just in case the factory reset request fails...
248 finishSetup();
249 }
250}