Moved functionality of DeviceOwnerProvActivity into service.
Service reports back to activity by broadcasts to a broadcast receiver.
Reporting can consist of either an error code or a success.
Change-Id: Ie8e916bd7e05f3179aca6c42f05103365ebc6b35
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 5072ecf..6a409e0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -87,5 +87,8 @@
android:launchMode="singleTop"
android:screenOrientation="nosensor" >
</activity>
+ <service
+ android:name="DeviceOwnerProvisioningService" >
+ </service>
</application>
</manifest>
diff --git a/res/layout/progress_device_owner.xml b/res/layout/progress_device_owner.xml
index 695c756..d09fff3 100644
--- a/res/layout/progress_device_owner.xml
+++ b/res/layout/progress_device_owner.xml
@@ -44,7 +44,6 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
- android:text="@string/device_owner_subtitle"
android:textSize="30sp" />
</LinearLayout>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 42412f7..b701c42 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -52,8 +52,16 @@
<!-- Device owner provisioning flow UI. -->
- <!-- Placeholder progress subtitle. [CHAR LIMIT=45] -->
- <string name="device_owner_subtitle">Placeholder text.</string>
+ <!-- Progress text indicating that Nfc message is being processed. [CHAR LIMIT=45] -->
+ <string name="progress_processing_nfc">Processing Nfc message.</string>
+ <!-- Progress text indicating that wifi is set up. [CHAR LIMIT=45] -->
+ <string name="progress_connect_to_wifi">Connecting to Wifi.</string>
+ <!-- Progress text indicating that the device admin package is being downloaded. [CHAR LIMIT=45] -->
+ <string name="progress_download">Downloading Device Admin package.</string>
+ <!-- Progress text indicating that the device admin package is being installed. [CHAR LIMIT=45] -->
+ <string name="progress_install">Installing Device Admin package.</string>
+ <!-- Progress text indicating that the device admin package is set as ownder. [CHAR LIMIT=45] -->
+ <string name="progress_set_owner">Setting Device Owner.</string>
<!-- Title of the error dialog. [CHAR LIMIT=45] -->
<string name="device_owner_error_title">Provisioning failed</string>
diff --git a/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java b/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
index 5f28079..a73cbc6 100644
--- a/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
+++ b/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
@@ -20,25 +20,16 @@
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.PackageManager;
+import android.content.IntentFilter;
import android.os.Bundle;
import android.provider.Settings.Global;
-import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
-
-import com.android.internal.app.LocalePicker;
-import com.android.managedprovisioning.task.AddWifiNetworkTask;
-import com.android.managedprovisioning.task.DownloadPackageTask;
-import com.android.managedprovisioning.task.InstallPackageTask;
-import com.android.managedprovisioning.task.SetDevicePolicyTask;
-
-import java.lang.Runnable;
-import java.util.Locale;
+import android.widget.TextView;
/**
* This activity starts device owner provisioning:
@@ -65,6 +56,9 @@
* </p>
*/
public class DeviceOwnerProvisioningActivity extends Activity {
+ private BroadcastReceiver mServiceMessageReceiver;
+ private TextView mProgressTextView;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -86,180 +80,53 @@
return;
}
- // Load the ProvisioningParams (from Nfc message in Intent).
- ProvisioningParams params;
- NfcMessageParser parser = new NfcMessageParser();
- Intent bumpData = getIntent();
- try {
- params = parser.parseNfcIntent(bumpData);
- } catch (NfcMessageParser.ParseException e) {
- ProvisionLogger.loge("Could not read Nfc data from intent", e);
- error(e.getErrorMessageId());
- return;
- }
-
- initializeProvisioningEnvironment(params);
-
- // TODO: update UI
final LayoutInflater inflater = getLayoutInflater();
final View contentView = inflater.inflate(R.layout.progress_device_owner, null);
setContentView(contentView);
+ mProgressTextView = (TextView) findViewById(R.id.dev_owner_prog_text);
- startDeviceOwnerProvisioning(params);
+ // Setup broadcast receiver for feedback from service.
+ mServiceMessageReceiver = new ServiceMessageReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS);
+ filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR);
+ filter.addAction(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE);
+ registerReceiver(mServiceMessageReceiver, filter);
+
+ // Start service.
+ Intent intent = new Intent(this, DeviceOwnerProvisioningService.class);
+ intent.putExtras(getIntent());
+ startService(intent);
}
- /**
- * This is the core method of this class. It goes through every provisioning step.
- */
- private void startDeviceOwnerProvisioning(ProvisioningParams params) {
- ProvisionLogger.logd("Starting device owner provisioning");
-
- // Construct Tasks. Do not start them yet.
- final AddWifiNetworkTask addWifiNetworkTask = new AddWifiNetworkTask(this, params.mWifiSsid,
- params.mWifiHidden, params.mWifiSecurityType, params.mWifiPassword,
- params.mWifiProxyHost, params.mWifiProxyPort, params.mWifiProxyBypassHosts);
- final DownloadPackageTask downloadPackageTask = new DownloadPackageTask(this,
- params.mDownloadLocation, params.mHash);
- final InstallPackageTask installPackageTask = new InstallPackageTask(this,
- params.mDeviceAdminPackageName, params.mAdminReceiver);
- final SetDevicePolicyTask setDevicePolicyTask = new SetDevicePolicyTask(this,
- params.mDeviceAdminPackageName, params.mAdminReceiver, params.mOwner);
-
- // Set callbacks.
- addWifiNetworkTask.setCallback(new AddWifiNetworkTask.Callback() {
- @Override
- public void onSuccess() {
- if (downloadPackageTask.downloadLocationWasProvided()) {
- downloadPackageTask.run();
+ private class ServiceMessageReceiver extends BroadcastReceiver
+ {
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ String action = intent.getAction();
+ if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS)) {
+ ProvisionLogger.logd("Successfully provisioned");
+ finish();
+ return;
+ } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR)) {
+ int errorCode = intent.getIntExtra(
+ DeviceOwnerProvisioningService.EXTRA_PROVISIONING_ERROR_ID_KEY, -1);
+ ProvisionLogger.logd("Error reported with code " + errorCode);
+ if (errorCode < 0) {
+ error(R.string.device_owner_error_general);
+ return;
} else {
- // TODO: replace success by starting the next task.
- onProvisioningSuccess();
+ error(errorCode);
+ }
+ } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE)) {
+ int progressMessage = intent.getIntExtra(
+ DeviceOwnerProvisioningService.EXTRA_PROGRESS_MESSAGE_ID_KEY, -1);
+ ProvisionLogger.logd("Progress update reported with code " + progressMessage);
+ if (progressMessage > 0) {
+ progressUpdate(progressMessage);
}
}
-
- @Override
- public void onError(){
- error(R.string.device_owner_error_wifi);
- }
- });
-
- downloadPackageTask.setCallback(new DownloadPackageTask.Callback() {
- @Override
- public void onSuccess() {
- String downloadLocation = downloadPackageTask.getDownloadedPackageLocation();
- Runnable cleanupRunnable = downloadPackageTask.getCleanUpDownloadRunnable();
- installPackageTask.run(downloadLocation, cleanupRunnable);
- }
-
- @Override
- public void onError(int errorCode) {
- switch(errorCode) {
- case DownloadPackageTask.ERROR_HASH_MISMATCH:
- error(R.string.device_owner_error_hash_mismatch);
- break;
- case DownloadPackageTask.ERROR_DOWNLOAD_FAILED:
- error(R.string.device_owner_error_download_failed);
- break;
- default:
- error(R.string.device_owner_error_general);
- break;
- }
- }
- });
-
- installPackageTask.setCallback(new InstallPackageTask.Callback() {
- @Override
- public void onSuccess() {
- setDevicePolicyTask.run();
- }
-
- @Override
- public void onError(int errorCode) {
- switch(errorCode) {
- case InstallPackageTask.ERROR_PACKAGE_INVALID:
- error(R.string.device_owner_error_package_invalid);
- break;
- case InstallPackageTask.ERROR_INSTALLATION_FAILED:
- error(R.string.device_owner_error_installation_failed);
- break;
- default:
- error(R.string.device_owner_error_general);
- break;
- }
- }
- });
-
- setDevicePolicyTask.setCallback(new SetDevicePolicyTask.Callback() {
- public void onSuccess() {
- // Done with provisioning. Success.
- onProvisioningSuccess();
- }
- public void onError(int errorCode) {
- switch(errorCode) {
- case SetDevicePolicyTask.ERROR_PACKAGE_NOT_INSTALLED:
- error(R.string.device_owner_error_package_not_installed);
- break;
- default:
- error(R.string.device_owner_error_general);
- break;
- }
- }
- });
-
-
- // Start first task, which starts next task in its callback, etc.
- if (addWifiNetworkTask.wifiCredentialsWereProvided()) {
- addWifiNetworkTask.run();
- } else {
- setDevicePolicyTask.run();
- }
- }
-
- public void onProvisioningSuccess() {
-
- // This package is no longer needed by the system, so disable it.
- PackageManager pkgMgr = getPackageManager();
- pkgMgr.setComponentEnabledSetting(
- new ComponentName(getPackageName(), getClass().getName()),
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
- finish();
- }
-
- private void initializeProvisioningEnvironment(ProvisioningParams params) {
- setTimeAndTimezone(params.mTimeZone, params.mLocalTime);
- setLocale(params.mLocale);
- }
-
- private void setTimeAndTimezone(String timeZone, Long localTime) {
- try {
- final AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- if (timeZone != null) {
- ProvisionLogger.logd("Setting time zone to " + timeZone);
- am.setTimeZone(timeZone);
- }
- if (localTime != null) {
- ProvisionLogger.logd("Setting time to " + localTime);
- am.setTime(localTime);
- }
- } catch (Exception e) {
- ProvisionLogger.loge("Alarm manager failed to set the system time/timezone.");
- // Do not stop provisioning process, but ignore this error.
- }
- }
-
- private void setLocale(Locale locale) {
- if (locale == null || locale.equals(Locale.getDefault())) {
- return;
- }
- try {
- ProvisionLogger.logd("Setting locale to " + locale);
- // If locale is different from current locale this results in a configuration change,
- // which will trigger the restarting of the activity.
- LocalePicker.updateLocale(locale);
- } catch (Exception e) {
- ProvisionLogger.loge("Failed to set the system locale.");
- // Do not stop provisioning process, but ignore this error.
}
}
@@ -268,7 +135,17 @@
// TODO: Handle this graciously by stopping the provisioning flow and cleaning up.
}
- public void error(int dialogMessage) {
+ @Override
+ public void onDestroy() {
+ unregisterReceiver(mServiceMessageReceiver);
+ super.onDestroy();
+ }
+
+ private void progressUpdate(int progressMessage) {
+ mProgressTextView.setText(progressMessage);
+ }
+
+ private void error(int dialogMessage) {
AlertDialog dlg = new AlertDialog.Builder(this)
.setTitle(R.string.device_owner_error_title)
.setMessage(dialogMessage)
diff --git a/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java b/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
new file mode 100644
index 0000000..127825c
--- /dev/null
+++ b/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.managedprovisioning;
+
+import android.app.AlarmManager;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.text.TextUtils;
+
+import com.android.internal.app.LocalePicker;
+import com.android.managedprovisioning.task.AddWifiNetworkTask;
+import com.android.managedprovisioning.task.DownloadPackageTask;
+import com.android.managedprovisioning.task.InstallPackageTask;
+import com.android.managedprovisioning.task.SetDevicePolicyTask;
+
+import java.lang.Runnable;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This service does the work for the DeviceOwnerProvisioningActivity.
+ * Feedback is sent back to the activity via intents.
+ *
+ * <p>
+ * If the corresponding activity is killed and restarted, the service is
+ * called twice. The service will not start the provisioning flow a second time, but instead
+ * send a status update to the activity.
+ * </p>
+ */
+public class DeviceOwnerProvisioningService extends Service {
+ public static final String ACTION_PROVISIONING_SUCCESS =
+ "com.android.managedprovisioning.provisioning_success";
+ public static final String ACTION_PROVISIONING_ERROR =
+ "com.android.managedprovisioning.error";
+ public static final String EXTRA_PROVISIONING_ERROR_ID_KEY = "ErrorMessageId";
+ public static final String ACTION_PROGRESS_UPDATE =
+ "com.android.managedprovisioning.progress_update";
+ public static final String EXTRA_PROGRESS_MESSAGE_ID_KEY = "ProgressMessageId";
+
+ private AtomicBoolean mProvisioningInFlight = new AtomicBoolean(false);
+ private int mLastProgressMessage;
+ private int mStartIdProvisioning;
+
+ @Override
+ public int onStartCommand(final Intent intent, int flags, int startId) {
+ if (mProvisioningInFlight.getAndSet(true)) {
+ ProvisionLogger.logd("Provisioning already in flight. Ignoring intent.");
+ sendProgressUpdateToActivity();
+ stopSelf(startId);
+ } else {
+ mStartIdProvisioning = startId;
+
+ // Do the work on a separate thread.
+ new Thread(new Runnable() {
+ public void run() {
+ // Load the ProvisioningParams (from Nfc message in Intent).
+ progressUpdate(R.string.progress_processing_nfc);
+ ProvisioningParams params;
+ NfcMessageParser parser = new NfcMessageParser();
+ try {
+ params = parser.parseNfcIntent(intent);
+ initializeProvisioningEnvironment(params);
+ startDeviceOwnerProvisioning(params);
+ } catch (NfcMessageParser.ParseException e) {
+ ProvisionLogger.loge("Could not read Nfc data from intent", e);
+ error(e.getErrorMessageId());
+ return;
+ }
+ }
+ }).start();
+ }
+ return START_NOT_STICKY;
+ }
+
+ /**
+ * This is the core method of this class. It goes through every provisioning step.
+ */
+ private void startDeviceOwnerProvisioning(ProvisioningParams params) {
+ ProvisionLogger.logd("Starting device owner provisioning");
+
+ // Construct Tasks. Do not start them yet.
+ final AddWifiNetworkTask addWifiNetworkTask = new AddWifiNetworkTask(this, params.mWifiSsid,
+ params.mWifiHidden, params.mWifiSecurityType, params.mWifiPassword,
+ params.mWifiProxyHost, params.mWifiProxyPort, params.mWifiProxyBypassHosts);
+ final DownloadPackageTask downloadPackageTask = new DownloadPackageTask(this,
+ params.mDownloadLocation, params.mHash);
+ final InstallPackageTask installPackageTask = new InstallPackageTask(this,
+ params.mDeviceAdminPackageName, params.mAdminReceiver);
+ final SetDevicePolicyTask setDevicePolicyTask = new SetDevicePolicyTask(this,
+ params.mDeviceAdminPackageName, params.mAdminReceiver, params.mOwner);
+
+ // Set callbacks.
+ addWifiNetworkTask.setCallback(new AddWifiNetworkTask.Callback() {
+ @Override
+ public void onSuccess() {
+ if (downloadPackageTask.downloadLocationWasProvided()) {
+ progressUpdate(R.string.progress_download);
+ downloadPackageTask.run();
+ } else {
+ progressUpdate(R.string.progress_set_owner);
+ setDevicePolicyTask.run();
+ }
+ }
+
+ @Override
+ public void onError(){
+ error(R.string.device_owner_error_wifi);
+ }
+ });
+
+ downloadPackageTask.setCallback(new DownloadPackageTask.Callback() {
+ @Override
+ public void onSuccess() {
+ String downloadLocation = downloadPackageTask.getDownloadedPackageLocation();
+ Runnable cleanupRunnable = downloadPackageTask.getCleanUpDownloadRunnable();
+ progressUpdate(R.string.progress_install);
+ installPackageTask.run(downloadLocation, cleanupRunnable);
+ }
+
+ @Override
+ public void onError(int errorCode) {
+ switch(errorCode) {
+ case DownloadPackageTask.ERROR_HASH_MISMATCH:
+ error(R.string.device_owner_error_hash_mismatch);
+ break;
+ case DownloadPackageTask.ERROR_DOWNLOAD_FAILED:
+ error(R.string.device_owner_error_download_failed);
+ break;
+ default:
+ error(R.string.device_owner_error_general);
+ break;
+ }
+ }
+ });
+
+ installPackageTask.setCallback(new InstallPackageTask.Callback() {
+ @Override
+ public void onSuccess() {
+ progressUpdate(R.string.progress_set_owner);
+ setDevicePolicyTask.run();
+ }
+
+ @Override
+ public void onError(int errorCode) {
+ switch(errorCode) {
+ case InstallPackageTask.ERROR_PACKAGE_INVALID:
+ error(R.string.device_owner_error_package_invalid);
+ break;
+ case InstallPackageTask.ERROR_INSTALLATION_FAILED:
+ error(R.string.device_owner_error_installation_failed);
+ break;
+ default:
+ error(R.string.device_owner_error_general);
+ break;
+ }
+ }
+ });
+
+ setDevicePolicyTask.setCallback(new SetDevicePolicyTask.Callback() {
+ public void onSuccess() {
+ // Done with provisioning. Success.
+ onProvisioningSuccess();
+ }
+ public void onError(int errorCode) {
+ switch(errorCode) {
+ case SetDevicePolicyTask.ERROR_PACKAGE_NOT_INSTALLED:
+ error(R.string.device_owner_error_package_not_installed);
+ break;
+ default:
+ error(R.string.device_owner_error_general);
+ break;
+ }
+ }
+ });
+
+
+ // Start first task, which starts next task in its callback, etc.
+ if (addWifiNetworkTask.wifiCredentialsWereProvided()) {
+ progressUpdate(R.string.progress_connect_to_wifi);
+ addWifiNetworkTask.run();
+ } else {
+ progressUpdate(R.string.progress_set_owner);
+ setDevicePolicyTask.run();
+ }
+ }
+
+ private void error(int dialogMessage) {
+ ProvisionLogger.logd("Reporting Error with code " + dialogMessage);
+ Intent intent = new Intent(ACTION_PROVISIONING_ERROR);
+ intent.putExtra(EXTRA_PROVISIONING_ERROR_ID_KEY, dialogMessage);
+ sendBroadcast(intent);
+ stopSelf(mStartIdProvisioning);
+ }
+
+ private void progressUpdate(int progressMessage) {
+ ProvisionLogger.logd("Reporting progress update with code " + progressMessage);
+ mLastProgressMessage = progressMessage;
+ sendProgressUpdateToActivity();
+ }
+
+ private void sendProgressUpdateToActivity() {
+ Intent intent = new Intent(ACTION_PROGRESS_UPDATE);
+ intent.putExtra(EXTRA_PROGRESS_MESSAGE_ID_KEY, mLastProgressMessage);
+ sendBroadcast(intent);
+ }
+
+ private void onProvisioningSuccess() {
+ sendBroadcast(new Intent(ACTION_PROVISIONING_SUCCESS));
+ stopSelf(mStartIdProvisioning);
+ }
+
+ private void initializeProvisioningEnvironment(ProvisioningParams params) {
+ setTimeAndTimezone(params.mTimeZone, params.mLocalTime);
+ setLocale(params.mLocale);
+ }
+
+ private void setTimeAndTimezone(String timeZone, Long localTime) {
+ try {
+ final AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ if (timeZone != null) {
+ ProvisionLogger.logd("Setting time zone to " + timeZone);
+ am.setTimeZone(timeZone);
+ }
+ if (localTime != null) {
+ ProvisionLogger.logd("Setting time to " + localTime);
+ am.setTime(localTime);
+ }
+ } catch (Exception e) {
+ ProvisionLogger.loge("Alarm manager failed to set the system time/timezone.");
+ // Do not stop provisioning process, but ignore this error.
+ }
+ }
+
+ private void setLocale(Locale locale) {
+ if (locale == null || locale.equals(Locale.getDefault())) {
+ return;
+ }
+ try {
+ ProvisionLogger.logd("Setting locale to " + locale);
+ // If locale is different from current locale this results in a configuration change,
+ // which will trigger the restarting of the activity.
+ LocalePicker.updateLocale(locale);
+ } catch (Exception e) {
+ ProvisionLogger.loge("Failed to set the system locale.");
+ // Do not stop provisioning process, but ignore this error.
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
+