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