blob: 0c25c13476aafc3d6e30f0872ae4661470c4c493 [file] [log] [blame]
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +01001/*
2 * Copyright 2014, 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.managedprovisioning;
18
19import android.app.AlarmManager;
20import android.app.Service;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.PackageManager;
25import android.os.IBinder;
Sander Alewijnse639e94c2014-05-01 16:07:51 +010026import android.provider.Settings.Global;
27import android.provider.Settings.Secure;
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +010028import android.text.TextUtils;
29
30import com.android.internal.app.LocalePicker;
31import com.android.managedprovisioning.task.AddWifiNetworkTask;
32import com.android.managedprovisioning.task.DownloadPackageTask;
33import com.android.managedprovisioning.task.InstallPackageTask;
34import com.android.managedprovisioning.task.SetDevicePolicyTask;
35
36import java.lang.Runnable;
37import java.util.Locale;
38import java.util.concurrent.atomic.AtomicBoolean;
39
40/**
41 * This service does the work for the DeviceOwnerProvisioningActivity.
42 * Feedback is sent back to the activity via intents.
43 *
44 * <p>
45 * If the corresponding activity is killed and restarted, the service is
46 * called twice. The service will not start the provisioning flow a second time, but instead
47 * send a status update to the activity.
48 * </p>
49 */
50public class DeviceOwnerProvisioningService extends Service {
Sander Alewijnse639e94c2014-05-01 16:07:51 +010051 /**
52 * Intent action to activate the CDMA phone connection by OTASP.
53 * This is not necessary for a GSM phone connection, which is activated automatically.
54 * String must agree with the constants in com.android.phone.InCallScreenShowActivation.
55 */
56 private static final String ACTION_PERFORM_CDMA_PROVISIONING =
57 "com.android.phone.PERFORM_CDMA_PROVISIONING";
58
59 // Intent actions for communication with DeviceOwnerProvisioningService.
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +010060 public static final String ACTION_PROVISIONING_SUCCESS =
61 "com.android.managedprovisioning.provisioning_success";
62 public static final String ACTION_PROVISIONING_ERROR =
63 "com.android.managedprovisioning.error";
64 public static final String EXTRA_PROVISIONING_ERROR_ID_KEY = "ErrorMessageId";
65 public static final String ACTION_PROGRESS_UPDATE =
66 "com.android.managedprovisioning.progress_update";
67 public static final String EXTRA_PROGRESS_MESSAGE_ID_KEY = "ProgressMessageId";
68
69 private AtomicBoolean mProvisioningInFlight = new AtomicBoolean(false);
70 private int mLastProgressMessage;
71 private int mStartIdProvisioning;
72
73 @Override
74 public int onStartCommand(final Intent intent, int flags, int startId) {
75 if (mProvisioningInFlight.getAndSet(true)) {
76 ProvisionLogger.logd("Provisioning already in flight. Ignoring intent.");
77 sendProgressUpdateToActivity();
78 stopSelf(startId);
79 } else {
80 mStartIdProvisioning = startId;
81
82 // Do the work on a separate thread.
83 new Thread(new Runnable() {
84 public void run() {
85 // Load the ProvisioningParams (from Nfc message in Intent).
86 progressUpdate(R.string.progress_processing_nfc);
87 ProvisioningParams params;
88 NfcMessageParser parser = new NfcMessageParser();
89 try {
90 params = parser.parseNfcIntent(intent);
91 initializeProvisioningEnvironment(params);
92 startDeviceOwnerProvisioning(params);
93 } catch (NfcMessageParser.ParseException e) {
94 ProvisionLogger.loge("Could not read Nfc data from intent", e);
95 error(e.getErrorMessageId());
96 return;
97 }
98 }
99 }).start();
100 }
101 return START_NOT_STICKY;
102 }
103
104 /**
105 * This is the core method of this class. It goes through every provisioning step.
106 */
107 private void startDeviceOwnerProvisioning(ProvisioningParams params) {
108 ProvisionLogger.logd("Starting device owner provisioning");
109
110 // Construct Tasks. Do not start them yet.
111 final AddWifiNetworkTask addWifiNetworkTask = new AddWifiNetworkTask(this, params.mWifiSsid,
112 params.mWifiHidden, params.mWifiSecurityType, params.mWifiPassword,
113 params.mWifiProxyHost, params.mWifiProxyPort, params.mWifiProxyBypassHosts);
114 final DownloadPackageTask downloadPackageTask = new DownloadPackageTask(this,
115 params.mDownloadLocation, params.mHash);
116 final InstallPackageTask installPackageTask = new InstallPackageTask(this,
117 params.mDeviceAdminPackageName, params.mAdminReceiver);
118 final SetDevicePolicyTask setDevicePolicyTask = new SetDevicePolicyTask(this,
119 params.mDeviceAdminPackageName, params.mAdminReceiver, params.mOwner);
120
121 // Set callbacks.
122 addWifiNetworkTask.setCallback(new AddWifiNetworkTask.Callback() {
123 @Override
124 public void onSuccess() {
125 if (downloadPackageTask.downloadLocationWasProvided()) {
126 progressUpdate(R.string.progress_download);
127 downloadPackageTask.run();
128 } else {
129 progressUpdate(R.string.progress_set_owner);
130 setDevicePolicyTask.run();
131 }
132 }
133
134 @Override
135 public void onError(){
136 error(R.string.device_owner_error_wifi);
137 }
138 });
139
140 downloadPackageTask.setCallback(new DownloadPackageTask.Callback() {
141 @Override
142 public void onSuccess() {
143 String downloadLocation = downloadPackageTask.getDownloadedPackageLocation();
144 Runnable cleanupRunnable = downloadPackageTask.getCleanUpDownloadRunnable();
145 progressUpdate(R.string.progress_install);
146 installPackageTask.run(downloadLocation, cleanupRunnable);
147 }
148
149 @Override
150 public void onError(int errorCode) {
151 switch(errorCode) {
152 case DownloadPackageTask.ERROR_HASH_MISMATCH:
153 error(R.string.device_owner_error_hash_mismatch);
154 break;
155 case DownloadPackageTask.ERROR_DOWNLOAD_FAILED:
156 error(R.string.device_owner_error_download_failed);
157 break;
158 default:
159 error(R.string.device_owner_error_general);
160 break;
161 }
162 }
163 });
164
165 installPackageTask.setCallback(new InstallPackageTask.Callback() {
166 @Override
167 public void onSuccess() {
168 progressUpdate(R.string.progress_set_owner);
169 setDevicePolicyTask.run();
170 }
171
172 @Override
173 public void onError(int errorCode) {
174 switch(errorCode) {
175 case InstallPackageTask.ERROR_PACKAGE_INVALID:
176 error(R.string.device_owner_error_package_invalid);
177 break;
178 case InstallPackageTask.ERROR_INSTALLATION_FAILED:
179 error(R.string.device_owner_error_installation_failed);
180 break;
181 default:
182 error(R.string.device_owner_error_general);
183 break;
184 }
185 }
186 });
187
188 setDevicePolicyTask.setCallback(new SetDevicePolicyTask.Callback() {
189 public void onSuccess() {
190 // Done with provisioning. Success.
191 onProvisioningSuccess();
192 }
193 public void onError(int errorCode) {
194 switch(errorCode) {
195 case SetDevicePolicyTask.ERROR_PACKAGE_NOT_INSTALLED:
196 error(R.string.device_owner_error_package_not_installed);
197 break;
198 default:
199 error(R.string.device_owner_error_general);
200 break;
201 }
202 }
203 });
204
205
206 // Start first task, which starts next task in its callback, etc.
207 if (addWifiNetworkTask.wifiCredentialsWereProvided()) {
208 progressUpdate(R.string.progress_connect_to_wifi);
209 addWifiNetworkTask.run();
210 } else {
211 progressUpdate(R.string.progress_set_owner);
212 setDevicePolicyTask.run();
213 }
214 }
215
216 private void error(int dialogMessage) {
217 ProvisionLogger.logd("Reporting Error with code " + dialogMessage);
218 Intent intent = new Intent(ACTION_PROVISIONING_ERROR);
219 intent.putExtra(EXTRA_PROVISIONING_ERROR_ID_KEY, dialogMessage);
220 sendBroadcast(intent);
221 stopSelf(mStartIdProvisioning);
222 }
223
224 private void progressUpdate(int progressMessage) {
225 ProvisionLogger.logd("Reporting progress update with code " + progressMessage);
226 mLastProgressMessage = progressMessage;
227 sendProgressUpdateToActivity();
228 }
229
230 private void sendProgressUpdateToActivity() {
231 Intent intent = new Intent(ACTION_PROGRESS_UPDATE);
232 intent.putExtra(EXTRA_PROGRESS_MESSAGE_ID_KEY, mLastProgressMessage);
233 sendBroadcast(intent);
234 }
235
236 private void onProvisioningSuccess() {
237 sendBroadcast(new Intent(ACTION_PROVISIONING_SUCCESS));
Sander Alewijnse639e94c2014-05-01 16:07:51 +0100238
239 // Skip the setup wizard.
240 Global.putInt(getContentResolver(), Global.DEVICE_PROVISIONED, 1);
241 Secure.putInt(getContentResolver(), Secure.USER_SETUP_COMPLETE, 1);
242
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +0100243 stopSelf(mStartIdProvisioning);
244 }
245
246 private void initializeProvisioningEnvironment(ProvisioningParams params) {
247 setTimeAndTimezone(params.mTimeZone, params.mLocalTime);
248 setLocale(params.mLocale);
Sander Alewijnse639e94c2014-05-01 16:07:51 +0100249
250 // Start CDMA activation to enable phone calls.
251 final Intent intent = new Intent(ACTION_PERFORM_CDMA_PROVISIONING);
252 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
253 ProvisionLogger.logv("Starting cdma activation activity");
254 startActivity(intent); // Activity will be a Nop if not a CDMA device.
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +0100255 }
256
257 private void setTimeAndTimezone(String timeZone, Long localTime) {
258 try {
259 final AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
260 if (timeZone != null) {
261 ProvisionLogger.logd("Setting time zone to " + timeZone);
262 am.setTimeZone(timeZone);
263 }
264 if (localTime != null) {
265 ProvisionLogger.logd("Setting time to " + localTime);
266 am.setTime(localTime);
267 }
268 } catch (Exception e) {
269 ProvisionLogger.loge("Alarm manager failed to set the system time/timezone.");
270 // Do not stop provisioning process, but ignore this error.
271 }
272 }
273
274 private void setLocale(Locale locale) {
275 if (locale == null || locale.equals(Locale.getDefault())) {
276 return;
277 }
278 try {
279 ProvisionLogger.logd("Setting locale to " + locale);
280 // If locale is different from current locale this results in a configuration change,
281 // which will trigger the restarting of the activity.
282 LocalePicker.updateLocale(locale);
283 } catch (Exception e) {
284 ProvisionLogger.loge("Failed to set the system locale.");
285 // Do not stop provisioning process, but ignore this error.
286 }
287 }
288
289 @Override
290 public IBinder onBind(Intent intent) {
291 return null;
292 }
293}
294