DeviceOwnerProvisioningService.java revision 91e23dddc899165feb982b181629c23dba310d8e
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.app.admin.DeviceInitializerStatus;
22import android.content.BroadcastReceiver;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.content.pm.PackageManager;
27import android.os.Bundle;
28import android.os.IBinder;
29import android.os.UserHandle;
30import android.support.v4.content.LocalBroadcastManager;
31import android.text.TextUtils;
32
33import com.android.internal.app.LocalePicker;
34import com.android.managedprovisioning.ProvisioningParams.PackageDownloadInfo;
35import com.android.managedprovisioning.proxy.BluetoothConnectionService;
36import com.android.managedprovisioning.task.AddWifiNetworkTask;
37import com.android.managedprovisioning.task.BluetoothConnectTask;
38import com.android.managedprovisioning.task.DeleteNonRequiredAppsTask;
39import com.android.managedprovisioning.task.DownloadPackageTask;
40import com.android.managedprovisioning.task.InstallPackageTask;
41import com.android.managedprovisioning.task.SetDevicePolicyTask;
42import com.android.managedprovisioning.task.WipeResetProtectionTask;
43
44import java.lang.Runnable;
45import java.util.Locale;
46
47/**
48 * This service does the work for the DeviceOwnerProvisioningActivity.
49 * Feedback is sent back to the activity via intents.
50 *
51 * <p>
52 * If the corresponding activity is killed and restarted, the service is
53 * called twice. The service will not start the provisioning flow a second time, but instead
54 * send a status update to the activity.
55 * </p>
56 */
57public class DeviceOwnerProvisioningService extends Service {
58    private static final boolean DEBUG = false; // To control logging.
59
60    private static final String DEVICE_OWNER = "deviceOwner";
61    private static final String DEVICE_INITIALIZER = "deviceInitializer";
62
63    /**
64     * Intent action to activate the CDMA phone connection by OTASP.
65     * This is not necessary for a GSM phone connection, which is activated automatically.
66     * String must agree with the constants in com.android.phone.InCallScreenShowActivation.
67     */
68    private static final String ACTION_PERFORM_CDMA_PROVISIONING =
69            "com.android.phone.PERFORM_CDMA_PROVISIONING";
70
71    // Intent actions and extras for communication from DeviceOwnerProvisioningService to Activity.
72    protected static final String EXTRA_PROVISIONING_PARAMS =
73            "ProvisioningParams";
74
75    // Intent actions and extras for communication from DeviceOwnerProvisioningActivity to Service.
76    protected static final String ACTION_PROVISIONING_SUCCESS =
77            "com.android.managedprovisioning.provisioning_success";
78    protected static final String ACTION_PROVISIONING_ERROR =
79            "com.android.managedprovisioning.error";
80    protected static final String EXTRA_USER_VISIBLE_ERROR_ID_KEY =
81            "UserVisibleErrorMessage-Id";
82    protected static final String ACTION_PROGRESS_UPDATE =
83            "com.android.managedprovisioning.progress_update";
84    protected static final String EXTRA_PROGRESS_MESSAGE_ID_KEY =
85            "ProgressMessageId";
86    protected static final String ACTION_REQUEST_WIFI_PICK =
87            "com.android.managedprovisioning.request_wifi_pick";
88
89    // Intent action used by the HomeReceiverActivity to notify this Service that a HOME intent was
90    // received, which indicates that the Setup wizard has closed after provisioning completed.
91    protected static final String ACTION_HOME_INDIRECT =
92            "com.android.managedprovisioning.home_indirect";
93
94    // Indicates whether provisioning has started.
95    private boolean mProvisioningInFlight = false;
96
97    // MessageId of the last progress message.
98    private int mLastProgressMessage = -1;
99
100    // MessageId of the last error message.
101    private int mLastErrorMessage = -1;
102
103    // Indicates whether provisioning has finished successfully (service waiting to stop).
104    private volatile boolean mDone = false;
105
106    // Provisioning tasks.
107    private BluetoothConnectTask mBluetoothConnectTask;
108    private AddWifiNetworkTask mAddWifiNetworkTask;
109    private WipeResetProtectionTask mWipeResetProtectionTask;
110    private DownloadPackageTask mDownloadPackageTask;
111    private InstallPackageTask mInstallPackageTask;
112    private SetDevicePolicyTask mSetDevicePolicyTask;
113    private DeleteNonRequiredAppsTask mDeleteNonRequiredAppsTask;
114
115    private ProvisioningParams mParams;
116
117    @Override
118    public int onStartCommand(final Intent intent, int flags, int startId) {
119        if (DEBUG) ProvisionLogger.logd("Device owner provisioning service ONSTARTCOMMAND.");
120
121        synchronized (this) { // Make operations on mProvisioningInFlight atomic.
122            if (mProvisioningInFlight) {
123                if (DEBUG) ProvisionLogger.logd("Provisioning already in flight.");
124
125                sendProgressUpdateToActivity();
126
127                // Send error message if currently in error state.
128                if (mLastErrorMessage >= 0) {
129                    sendError();
130                }
131
132                // Send success if provisioning was successful.
133                if (mDone) {
134                    onProvisioningSuccess();
135                }
136            } else {
137                mProvisioningInFlight = true;
138                if (DEBUG) ProvisionLogger.logd("First start of the service.");
139                progressUpdate(R.string.progress_data_process);
140
141                // Load the ProvisioningParams (from message in Intent).
142                mParams = (ProvisioningParams) intent.getParcelableExtra(EXTRA_PROVISIONING_PARAMS);
143
144                // Do the work on a separate thread.
145                new Thread(new Runnable() {
146                        public void run() {
147                            initializeProvisioningEnvironment(mParams);
148                            startDeviceOwnerProvisioning(mParams);
149                        }
150                    }).start();
151            }
152        }
153        return START_NOT_STICKY;
154    }
155
156    /**
157     * This is the core method of this class. It goes through every provisioning step.
158     * Each task checks if it is required and executes if it is.
159     */
160    private void startDeviceOwnerProvisioning(final ProvisioningParams params) {
161        if (DEBUG) ProvisionLogger.logd("Starting device owner provisioning");
162
163        // Construct Tasks. Do not start them yet.
164        mBluetoothConnectTask = new BluetoothConnectTask(this, params.bluetoothInfo,
165                !TextUtils.isEmpty(params.wifiInfo.ssid),
166                new BluetoothConnectTask.Callback() {
167                        @Override
168                        public void onSuccess() {
169                            progressUpdate(R.string.progress_connect_to_wifi);
170                            mAddWifiNetworkTask.run();
171                        }
172                        @Override
173                        public void onError() {
174                            error(R.string.device_owner_error_bluetooth);
175                        }
176                });
177
178        mAddWifiNetworkTask = new AddWifiNetworkTask(this, params.wifiInfo,
179                new AddWifiNetworkTask.Callback() {
180                       @Override
181                       public void onSuccess() {
182                           progressUpdate(R.string.progress_wipe_frp);
183                           mWipeResetProtectionTask.run();
184                       }
185
186                       @Override
187                       public void onError(){
188                           error(R.string.device_owner_error_wifi);
189                           statusUpdate(DeviceInitializerStatus.STATUS_ERROR_CONNECT_WIFI);
190                           }
191                       });
192
193        mWipeResetProtectionTask = new WipeResetProtectionTask(this, params.frpChallengeBundle,
194                new WipeResetProtectionTask.Callback() {
195                    @Override
196                    public void onSuccess() {
197                        progressUpdate(R.string.progress_download);
198                        mDownloadPackageTask.run();
199                    }
200                    @Override
201                    public void onError() {
202                        error(R.string.device_owner_error_frp);
203                        statusUpdate(DeviceInitializerStatus.
204                                STATUS_ERROR_RESET_PROTECTION_BLOCKING_PROVISIONING);
205                    }
206                });
207
208        mDownloadPackageTask = new DownloadPackageTask(this,
209                new DownloadPackageTask.Callback() {
210                        @Override
211                        public void onSuccess() {
212                            progressUpdate(R.string.progress_install);
213                            mInstallPackageTask.addInstallIfNecessary(
214                                    params.inferDeviceAdminPackageName(),
215                                    mDownloadPackageTask.getDownloadedPackageLocation(
216                                            DEVICE_OWNER));
217                            mInstallPackageTask.addInstallIfNecessary(
218                                    params.getDeviceInitializerPackageName(),
219                                    mDownloadPackageTask.getDownloadedPackageLocation(
220                                            DEVICE_INITIALIZER));
221                            mInstallPackageTask.run();
222                        }
223
224                        @Override
225                        public void onError(int errorCode) {
226                            switch(errorCode) {
227                                case DownloadPackageTask.ERROR_HASH_MISMATCH:
228                                    error(R.string.device_owner_error_hash_mismatch);
229                                    break;
230                                case DownloadPackageTask.ERROR_DOWNLOAD_FAILED:
231                                    error(R.string.device_owner_error_download_failed);
232                                    break;
233                                default:
234                                    error(R.string.device_owner_error_general);
235                                    break;
236                            }
237                            statusUpdate(DeviceInitializerStatus.STATUS_ERROR_DOWNLOAD_PACKAGE);
238                        }
239                    });
240
241        // Add packages to download to the DownloadPackageTask.
242        mDownloadPackageTask.addDownloadIfNecessary(params.inferDeviceAdminPackageName(),
243                params.deviceAdminDownloadInfo, DEVICE_OWNER);
244        mDownloadPackageTask.addDownloadIfNecessary(params.getDeviceInitializerPackageName(),
245                params.deviceInitializerDownloadInfo, DEVICE_INITIALIZER);
246
247        mInstallPackageTask = new InstallPackageTask(this,
248                new InstallPackageTask.Callback() {
249                    @Override
250                    public void onSuccess() {
251                        progressUpdate(R.string.progress_set_owner);
252                        try {
253                            // Now that the app has been installed, we can look for the device admin
254                            // component in it.
255                            mSetDevicePolicyTask.run(mParams.inferDeviceAdminComponentName(
256                                    DeviceOwnerProvisioningService.this));
257                        } catch (Utils.IllegalProvisioningArgumentException e) {
258                            error(R.string.device_owner_error_general);
259                            ProvisionLogger.loge("Failed to infer the device admin component name",
260                                    e);
261                            return;
262                        }
263                    }
264
265                    @Override
266                    public void onError(int errorCode) {
267                        switch(errorCode) {
268                            case InstallPackageTask.ERROR_PACKAGE_INVALID:
269                                error(R.string.device_owner_error_package_invalid);
270                                break;
271                            case InstallPackageTask.ERROR_INSTALLATION_FAILED:
272                                error(R.string.device_owner_error_installation_failed);
273                                break;
274                            case InstallPackageTask.ERROR_PACKAGE_NAME_INVALID:
275                                error(R.string.device_owner_error_package_name_invalid);
276                                break;
277                            default:
278                                error(R.string.device_owner_error_general);
279                                break;
280                        }
281                        statusUpdate(DeviceInitializerStatus.STATUS_ERROR_INSTALL_PACKAGE);
282                    }
283                });
284
285        mSetDevicePolicyTask = new SetDevicePolicyTask(this,
286                getResources().getString(R.string.default_owned_device_username),
287                params.deviceInitializerComponentName,
288                new SetDevicePolicyTask.Callback() {
289                    @Override
290                    public void onSuccess() {
291                        mDeleteNonRequiredAppsTask.run();
292                    }
293
294                    @Override
295                    public void onError(int errorCode) {
296                        switch(errorCode) {
297                            case SetDevicePolicyTask.ERROR_PACKAGE_NOT_INSTALLED:
298                                error(R.string.device_owner_error_package_not_installed);
299                                break;
300                            case SetDevicePolicyTask.ERROR_NO_RECEIVER:
301                                error(R.string.device_owner_error_package_invalid);
302                                break;
303                            default:
304                                error(R.string.device_owner_error_general);
305                                break;
306                        }
307                        statusUpdate(DeviceInitializerStatus.STATUS_ERROR_SET_DEVICE_POLICY);
308                    }
309                });
310
311        mDeleteNonRequiredAppsTask = new DeleteNonRequiredAppsTask(
312                this, params.inferDeviceAdminPackageName(), R.array.required_apps_managed_device,
313                R.array.vendor_required_apps_managed_device, true /* creating new profile */,
314                UserHandle.USER_OWNER, params.leaveAllSystemAppsEnabled,
315                new DeleteNonRequiredAppsTask.Callback() {
316                    @Override
317                    public void onSuccess() {
318                        // Done with provisioning. Success.
319                        onProvisioningSuccess();
320                    }
321
322                    @Override
323                    public void onError() {
324                        error(R.string.device_owner_error_general);
325                        statusUpdate(DeviceInitializerStatus.STATUS_ERROR_DELETE_APPS);
326                    }
327                });
328
329        // Start first task, which starts next task in its callback, etc.
330        progressUpdate(R.string.progress_start_bluetooth);
331        mBluetoothConnectTask.run();
332    }
333
334    private void error(int dialogMessage) {
335        mLastErrorMessage = dialogMessage;
336        sendError();
337        // Wait for stopService() call from the activity.
338    }
339
340    private void statusUpdate(int statusCode) {
341        BluetoothConnectionService.sendStatusUpdate(this, statusCode);
342    }
343
344    private void sendError() {
345        if (DEBUG) {
346            ProvisionLogger.logd("Reporting Error: " + getResources()
347                .getString(mLastErrorMessage));
348        }
349        Intent intent = new Intent(ACTION_PROVISIONING_ERROR);
350        intent.setClass(this, DeviceOwnerProvisioningActivity.ServiceMessageReceiver.class);
351        intent.putExtra(EXTRA_USER_VISIBLE_ERROR_ID_KEY, mLastErrorMessage);
352        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
353    }
354
355    private void progressUpdate(int progressMessage) {
356        if (DEBUG) {
357            ProvisionLogger.logd("Reporting progress update: " + getResources()
358                .getString(progressMessage));
359        }
360        mLastProgressMessage = progressMessage;
361        sendProgressUpdateToActivity();
362    }
363
364    private void sendProgressUpdateToActivity() {
365        Intent intent = new Intent(ACTION_PROGRESS_UPDATE);
366        intent.putExtra(EXTRA_PROGRESS_MESSAGE_ID_KEY, mLastProgressMessage);
367        intent.setClass(this, DeviceOwnerProvisioningActivity.ServiceMessageReceiver.class);
368        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
369    }
370
371    private void onProvisioningSuccess() {
372        if (DEBUG) ProvisionLogger.logd("Reporting success.");
373        mDone = true;
374
375        // Persist mParams so HomeReceiverActivity can later retrieve them to finalize provisioning.
376        // This is necessary to deal with accidental reboots during DIA setup, which happens between
377        // the end of this method and HomeReceiverActivity captures the home intent.
378        IntentStore store = BootReminder.getDeviceOwnerFinalizingIntentStore(this);
379        Bundle resumeBundle = new Bundle();
380        (new MessageParser()).addProvisioningParamsToBundle(resumeBundle, mParams);
381        store.save(resumeBundle);
382
383        // Enable the HomeReceiverActivity, since the DeviceOwnerProvisioningActivity will shutdown
384        // the Setup wizard soon, which will result in a home intent that should be caught by the
385        // HomeReceiverActivity.
386        PackageManager pm = getPackageManager();
387        pm.setComponentEnabledSetting(new ComponentName(DeviceOwnerProvisioningService.this,
388                        HomeReceiverActivity.class), PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
389                PackageManager.DONT_KILL_APP);
390
391        Intent successIntent = new Intent(ACTION_PROVISIONING_SUCCESS);
392        successIntent.setClass(this, DeviceOwnerProvisioningActivity.ServiceMessageReceiver.class);
393        LocalBroadcastManager.getInstance(this).sendBroadcast(successIntent);
394        // Wait for stopService() call from the activity.
395    }
396
397    private void initializeProvisioningEnvironment(ProvisioningParams params) {
398        setTimeAndTimezone(params.timeZone, params.localTime);
399        setLocale(params.locale);
400
401        // Start CDMA activation to enable phone calls.
402        final Intent intent = new Intent(ACTION_PERFORM_CDMA_PROVISIONING);
403        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
404        if (DEBUG) ProvisionLogger.logd("Starting cdma activation activity");
405        startActivity(intent); // Activity will be a Nop if not a CDMA device.
406    }
407
408    private void setTimeAndTimezone(String timeZone, long localTime) {
409        try {
410            final AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
411            if (timeZone != null) {
412                if (DEBUG) ProvisionLogger.logd("Setting time zone to " + timeZone);
413                am.setTimeZone(timeZone);
414            }
415            if (localTime > 0) {
416                if (DEBUG) ProvisionLogger.logd("Setting time to " + localTime);
417                am.setTime(localTime);
418            }
419        } catch (Exception e) {
420            ProvisionLogger.loge("Alarm manager failed to set the system time/timezone.");
421            // Do not stop provisioning process, but ignore this error.
422        }
423    }
424
425    private void setLocale(Locale locale) {
426        if (locale == null || locale.equals(Locale.getDefault())) {
427            return;
428        }
429        try {
430            if (DEBUG) ProvisionLogger.logd("Setting locale to " + locale);
431            // If locale is different from current locale this results in a configuration change,
432            // which will trigger the restarting of the activity.
433            LocalePicker.updateLocale(locale);
434        } catch (Exception e) {
435            ProvisionLogger.loge("Failed to set the system locale.");
436            // Do not stop provisioning process, but ignore this error.
437        }
438    }
439
440    @Override
441    public void onCreate () {
442        if (DEBUG) ProvisionLogger.logd("Device owner provisioning service ONCREATE.");
443    }
444
445    @Override
446    public void onDestroy () {
447        if (DEBUG) ProvisionLogger.logd("Device owner provisioning service ONDESTROY");
448        if (mAddWifiNetworkTask != null) {
449            mAddWifiNetworkTask.cleanUp();
450        }
451        if (mDownloadPackageTask != null) {
452            mDownloadPackageTask.cleanUp();
453        }
454        if (mBluetoothConnectTask != null) {
455            mBluetoothConnectTask.cleanUp();
456        }
457    }
458
459    @Override
460    public IBinder onBind(Intent intent) {
461        return null;
462    }
463}
464
465