DeviceOwnerProvisioningService.java revision 226d36cdcf8f08b2fd6a853a92ca24d570dc58d3
1/*
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.os.Bundle;
25import android.os.IBinder;
26import android.support.v4.content.LocalBroadcastManager;
27
28import com.android.internal.app.LocalePicker;
29import com.android.managedprovisioning.task.AddWifiNetworkTask;
30import com.android.managedprovisioning.task.DeleteNonRequiredAppsTask;
31import com.android.managedprovisioning.task.DownloadPackageTask;
32import com.android.managedprovisioning.task.InstallPackageTask;
33import com.android.managedprovisioning.task.SetDevicePolicyTask;
34
35import java.lang.Runnable;
36import java.util.Locale;
37
38/**
39 * This service does the work for the DeviceOwnerProvisioningActivity.
40 * Feedback is sent back to the activity via intents.
41 *
42 * <p>
43 * If the corresponding activity is killed and restarted, the service is
44 * called twice. The service will not start the provisioning flow a second time, but instead
45 * send a status update to the activity.
46 * </p>
47 */
48public class DeviceOwnerProvisioningService extends Service {
49    /**
50     * Intent action to activate the CDMA phone connection by OTASP.
51     * This is not necessary for a GSM phone connection, which is activated automatically.
52     * String must agree with the constants in com.android.phone.InCallScreenShowActivation.
53     */
54    private static final String ACTION_PERFORM_CDMA_PROVISIONING =
55            "com.android.phone.PERFORM_CDMA_PROVISIONING";
56
57    // Intent actions for communication with DeviceOwnerProvisioningService.
58    protected static final String ACTION_PROVISIONING_SUCCESS =
59            "com.android.managedprovisioning.provisioning_success";
60    protected static final String ACTION_PROVISIONING_ERROR =
61            "com.android.managedprovisioning.error";
62    protected static final String EXTRA_USER_VISIBLE_ERROR_ID_KEY =
63            "UserVisibleErrorMessage-Id";
64    protected static final String ACTION_PROGRESS_UPDATE =
65            "com.android.managedprovisioning.progress_update";
66    protected static final String EXTRA_PROGRESS_MESSAGE_ID_KEY =
67            "ProgressMessageId";
68    protected static final String EXTRA_PROVISIONING_PARAMS =
69            "ProvisioningParams";
70
71    private boolean mProvisioningInFlight = false;
72    private int mLastProgressMessage = -1;
73    private int mLastErrorMessage = -1;
74    private boolean mDone = false;
75
76    // Provisioning tasks.
77    private AddWifiNetworkTask mAddWifiNetworkTask;
78    private DownloadPackageTask mDownloadPackageTask;
79    private InstallPackageTask mInstallPackageTask;
80    private SetDevicePolicyTask mSetDevicePolicyTask;
81    private DeleteNonRequiredAppsTask mDeleteNonRequiredAppsTask;
82
83    private ProvisioningParams mParams;
84
85    @Override
86    public int onStartCommand(final Intent intent, int flags, int startId) {
87        ProvisionLogger.logd("Device owner provisioning service ONSTARTCOMMAND.");
88
89        synchronized (this) { // Make operations on mProvisioningInFlight atomic.
90            if (mProvisioningInFlight) {
91                ProvisionLogger.logd("Provisioning already in flight.");
92
93                sendProgressUpdateToActivity();
94
95                // Send error message if currently in error state.
96                if (mLastErrorMessage >= 0) {
97                    sendError();
98                }
99
100                if (mDone) {
101                    onProvisioningSuccess(mParams.mDeviceAdminPackageName);
102                }
103            } else {
104                mProvisioningInFlight = true;
105                ProvisionLogger.logd("First start of the service.");
106                progressUpdate(R.string.progress_data_process);
107
108                // Load the ProvisioningParams (from message in Intent).
109                mParams = (ProvisioningParams) intent.getParcelableExtra(EXTRA_PROVISIONING_PARAMS);
110
111                // Do the work on a separate thread.
112                new Thread(new Runnable() {
113                        public void run() {
114                            initializeProvisioningEnvironment(mParams);
115                            startDeviceOwnerProvisioning(mParams);
116                        }
117                    }).start();
118            }
119        }
120        return START_NOT_STICKY;
121    }
122
123    /**
124     * This is the core method of this class. It goes through every provisioning step.
125     */
126    private void startDeviceOwnerProvisioning(final ProvisioningParams params) {
127        ProvisionLogger.logd("Starting device owner provisioning");
128
129        // Construct Tasks. Do not start them yet.
130        mAddWifiNetworkTask = new AddWifiNetworkTask(this, params.mWifiSsid,
131                params.mWifiHidden, params.mWifiSecurityType, params.mWifiPassword,
132                params.mWifiProxyHost, params.mWifiProxyPort, params.mWifiProxyBypassHosts,
133                params.mWifiPacUrl, new AddWifiNetworkTask.Callback() {
134                        @Override
135                        public void onSuccess() {
136                            if (mDownloadPackageTask.downloadLocationWasProvided()) {
137                                progressUpdate(R.string.progress_download);
138                                mDownloadPackageTask.run();
139                            } else {
140                                progressUpdate(R.string.progress_set_owner);
141                                mSetDevicePolicyTask.run();
142                            }
143                        }
144
145                        @Override
146                        public void onError(){
147                            error(R.string.device_owner_error_wifi);
148                        }
149                });
150
151        mDownloadPackageTask = new DownloadPackageTask(this,
152                params.mDownloadLocation, params.mHash,
153                new DownloadPackageTask.Callback() {
154                        @Override
155                        public void onSuccess() {
156                            String downloadLocation =
157                                    mDownloadPackageTask.getDownloadedPackageLocation();
158                            progressUpdate(R.string.progress_install);
159                            mInstallPackageTask.run(downloadLocation);
160                        }
161
162                        @Override
163                        public void onError(int errorCode) {
164                            switch(errorCode) {
165                                case DownloadPackageTask.ERROR_HASH_MISMATCH:
166                                    error(R.string.device_owner_error_hash_mismatch);
167                                    break;
168                                case DownloadPackageTask.ERROR_DOWNLOAD_FAILED:
169                                    error(R.string.device_owner_error_download_failed);
170                                    break;
171                                default:
172                                    error(R.string.device_owner_error_general);
173                                    break;
174                            }
175                        }
176                    });
177
178        mInstallPackageTask = new InstallPackageTask(this,
179                params.mDeviceAdminPackageName,
180                new InstallPackageTask.Callback() {
181                    @Override
182                    public void onSuccess() {
183                        progressUpdate(R.string.progress_set_owner);
184                        mSetDevicePolicyTask.run();
185                    }
186
187                    @Override
188                    public void onError(int errorCode) {
189                        switch(errorCode) {
190                            case InstallPackageTask.ERROR_PACKAGE_INVALID:
191                                error(R.string.device_owner_error_package_invalid);
192                                break;
193                            case InstallPackageTask.ERROR_INSTALLATION_FAILED:
194                                error(R.string.device_owner_error_installation_failed);
195                                break;
196                            default:
197                                error(R.string.device_owner_error_general);
198                                break;
199                        }
200                    }
201                });
202
203        mSetDevicePolicyTask = new SetDevicePolicyTask(this,
204                params.mDeviceAdminPackageName, params.mOwner,
205                new SetDevicePolicyTask.Callback() {
206                    @Override
207                    public void onSuccess() {
208                        mDeleteNonRequiredAppsTask.run();
209                    }
210
211                    @Override
212                    public void onError(int errorCode) {
213                        switch(errorCode) {
214                            case SetDevicePolicyTask.ERROR_PACKAGE_NOT_INSTALLED:
215                                error(R.string.device_owner_error_package_not_installed);
216                                break;
217                            case SetDevicePolicyTask.ERROR_NO_RECEIVER:
218                                error(R.string.device_owner_error_missing_receiver);
219                                break;
220                            default:
221                                error(R.string.device_owner_error_general);
222                                break;
223                        }
224                    }
225                });
226
227        mDeleteNonRequiredAppsTask =  new DeleteNonRequiredAppsTask(
228                this, params.mDeviceAdminPackageName, 0 /* primary user's UserId */,
229                R.array.required_apps_managed_device, R.array.vendor_required_apps_managed_device,
230                new DeleteNonRequiredAppsTask.Callback() {
231                    public void onSuccess() {
232                        // Done with provisioning. Success.
233                        onProvisioningSuccess(params.mDeviceAdminPackageName);
234                    }
235
236                    @Override
237                    public void onError() {
238                        error(R.string.device_owner_error_general);
239                    };
240                });
241
242        // Start first task, which starts next task in its callback, etc.
243        if (mAddWifiNetworkTask.wifiCredentialsWereProvided()) {
244            progressUpdate(R.string.progress_connect_to_wifi);
245            mAddWifiNetworkTask.run();
246        } else {
247            progressUpdate(R.string.progress_set_owner);
248            mSetDevicePolicyTask.run();
249        }
250    }
251
252    private void error(int dialogMessage) {
253        mLastErrorMessage = dialogMessage;
254        sendError();
255        // Wait for stopService() call from the activity.
256    }
257
258    private void sendError() {
259        ProvisionLogger.logd("Reporting Error: " + getResources().getString(mLastErrorMessage));
260        Intent intent = new Intent(ACTION_PROVISIONING_ERROR);
261        intent.setClass(this, DeviceOwnerProvisioningActivity.ServiceMessageReceiver.class);
262        intent.putExtra(EXTRA_USER_VISIBLE_ERROR_ID_KEY, mLastErrorMessage);
263        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
264    }
265
266    private void progressUpdate(int progressMessage) {
267        ProvisionLogger.logd("Reporting progress update: "
268                + getResources().getString(progressMessage));
269        mLastProgressMessage = progressMessage;
270        sendProgressUpdateToActivity();
271    }
272
273    private void sendProgressUpdateToActivity() {
274        Intent intent = new Intent(ACTION_PROGRESS_UPDATE);
275        intent.putExtra(EXTRA_PROGRESS_MESSAGE_ID_KEY, mLastProgressMessage);
276        intent.setClass(this, DeviceOwnerProvisioningActivity.ServiceMessageReceiver.class);
277        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
278    }
279
280
281    private void onProvisioningSuccess(String deviceAdminPackage) {
282        ProvisionLogger.logv("Reporting success.");
283        mDone = true;
284        Intent successIntent = new Intent(ACTION_PROVISIONING_SUCCESS);
285        successIntent.setClass(this, DeviceOwnerProvisioningActivity.ServiceMessageReceiver.class);
286        LocalBroadcastManager.getInstance(this).sendBroadcast(successIntent);
287        // Wait for stopService() call from the activity.
288    }
289
290    private void initializeProvisioningEnvironment(ProvisioningParams params) {
291        setTimeAndTimezone(params.mTimeZone, params.mLocalTime);
292        setLocale(params.mLocale);
293
294        // Start CDMA activation to enable phone calls.
295        final Intent intent = new Intent(ACTION_PERFORM_CDMA_PROVISIONING);
296        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
297        ProvisionLogger.logv("Starting cdma activation activity");
298        startActivity(intent); // Activity will be a Nop if not a CDMA device.
299    }
300
301    private void setTimeAndTimezone(String timeZone, long localTime) {
302        try {
303            final AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
304            if (timeZone != null) {
305                ProvisionLogger.logd("Setting time zone to " + timeZone);
306                am.setTimeZone(timeZone);
307            }
308            if (localTime > 0) {
309                ProvisionLogger.logd("Setting time to " + localTime);
310                am.setTime(localTime);
311            }
312        } catch (Exception e) {
313            ProvisionLogger.loge("Alarm manager failed to set the system time/timezone.");
314            // Do not stop provisioning process, but ignore this error.
315        }
316    }
317
318    private void setLocale(Locale locale) {
319        if (locale == null || locale.equals(Locale.getDefault())) {
320            return;
321        }
322        try {
323            ProvisionLogger.logd("Setting locale to " + locale);
324            // If locale is different from current locale this results in a configuration change,
325            // which will trigger the restarting of the activity.
326            LocalePicker.updateLocale(locale);
327        } catch (Exception e) {
328            ProvisionLogger.loge("Failed to set the system locale.");
329            // Do not stop provisioning process, but ignore this error.
330        }
331    }
332
333    @Override
334    public void onCreate () {
335        ProvisionLogger.logd("Device owner provisioning service ONCREATE.");
336    }
337
338    @Override
339    public void onDestroy () {
340        ProvisionLogger.logd("Device owner provisioning service ONDESTROY");
341        if (mAddWifiNetworkTask != null) {
342            mAddWifiNetworkTask.cleanUp();
343        }
344        if (mDownloadPackageTask != null) {
345            mDownloadPackageTask.cleanUp();
346        }
347    }
348
349    @Override
350    public IBinder onBind(Intent intent) {
351        return null;
352    }
353}
354
355