1 /*
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 
17 package com.android.provision;
18 
19 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
20 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
21 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION;
22 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM;
23 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TRIGGER;
24 
25 import static com.android.provision.Utils.DEFAULT_SETTINGS_PROVISION_DO_MODE;
26 import static com.android.provision.Utils.SETTINGS_PROVISION_DO_MODE;
27 import static com.android.provision.Utils.TAG;
28 import static com.android.provision.Utils.getSettings;
29 
30 import android.app.Activity;
31 import android.app.AlertDialog;
32 import android.app.admin.DevicePolicyManager;
33 import android.content.ComponentName;
34 import android.content.Intent;
35 import android.content.pm.PackageManager;
36 import android.os.Bundle;
37 import android.provider.Settings;
38 import android.util.Log;
39 
40 /**
41  * 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>
63  */
64 public class DefaultActivity extends Activity {
65 
66     // 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 
76     @Override
onCreate(Bundle icicle)77     protected void onCreate(Bundle icicle) {
78         super.onCreate(icicle);
79 
80         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 
finishSetup()90     private void finishSetup() {
91         setProvisioningState();
92         disableSelfAndFinish();
93     }
94 
setProvisioningState()95     private void setProvisioningState() {
96         Log.i(TAG, "Setting provisioning state");
97         // Add a persistent setting to allow other apps to know the device has been provisioned.
98         Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
99         Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);
100     }
101 
disableSelfAndFinish()102     private void disableSelfAndFinish() {
103         // remove this activity from the package manager.
104         PackageManager pm = getPackageManager();
105         ComponentName name = new ComponentName(this, DefaultActivity.class);
106         Log.i(TAG, "Disabling itself (" + name + ")");
107         pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
108                 PackageManager.DONT_KILL_APP);
109         // terminate the activity.
110         finish();
111     }
112 
provisionDeviceOwner()113     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
onActivityResult(int requestCode, int resultCode, Intent data)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 
onProvisioningStep1Result(int resultCode)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 
onProvisioningStep2Result(int requestCode, int resultCode)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 
resultCodeToString(int resultCode)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 
showErrorMessage(String message)225     private void showErrorMessage(String message) {
226         Log.e(TAG, "Error: " + message);
227     }
228 
factoryReset(String reason)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 
sendFactoryResetIntent(String reason)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 }
251