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