summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulia Reynolds <juliacr@google.com>2015-02-11 12:34:08 -0500
committerJulia Reynolds <juliacr@google.com>2015-03-04 10:25:43 -0500
commit20118f18c11c01c7743b5646cc1a0039c2e90037 (patch)
tree85e6b2a6d9e82b3266965a69d2e315c36c4338eb
parentd5fe9f60b3af05f91ca0548c4fb91745dd2a85b1 (diff)
downloadframeworks_base-20118f18c11c01c7743b5646cc1a0039c2e90037.zip
frameworks_base-20118f18c11c01c7743b5646cc1a0039c2e90037.tar.gz
frameworks_base-20118f18c11c01c7743b5646cc1a0039c2e90037.tar.bz2
Allow the device initializer to perform user setup tasks.
A device initializer is an application that is allowed to run during user provisioning on device owner devices. During device provisioning (or, user provisioning of the first user of the device), a device initializer is granted device owner permissions. During secondary user provisioning, a device initializer is granted profile owner permissions. Once provisioning is complete for a user, all elevated permissions are removed from the device initializer and the device admin component of the app is disabled. Bug: 19230954 Change-Id: Ib6725fb3b09bb21e4198a5dc0b445ccebb40b27e
-rw-r--r--api/current.txt11
-rw-r--r--api/system-current.txt12
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java62
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java167
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl6
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java47
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java159
7 files changed, 443 insertions, 21 deletions
diff --git a/api/current.txt b/api/current.txt
index 707999b..163e9d0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5429,6 +5429,7 @@ package android.app.admin {
method public void onPasswordFailed(android.content.Context, android.content.Intent);
method public void onPasswordSucceeded(android.content.Context, android.content.Intent);
method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent);
+ method public void onReadyForUserInitialization(android.content.Context, android.content.Intent);
method public void onReceive(android.content.Context, android.content.Intent);
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
@@ -5440,6 +5441,7 @@ package android.app.admin {
field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED";
field public static final java.lang.String ACTION_PASSWORD_SUCCEEDED = "android.app.action.ACTION_PASSWORD_SUCCEEDED";
field public static final java.lang.String ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.PROFILE_PROVISIONING_COMPLETE";
+ field public static final java.lang.String ACTION_READY_FOR_USER_INITIALIZATION = "android.app.action.READY_FOR_USER_INITIALIZATION";
field public static final java.lang.String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE";
@@ -5451,6 +5453,7 @@ package android.app.admin {
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
method public void addUserRestriction(android.content.ComponentName, java.lang.String);
method public void clearCrossProfileIntentFilters(android.content.ComponentName);
+ method public void clearDeviceInitializerApp();
method public void clearDeviceOwnerApp(java.lang.String);
method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
@@ -5489,11 +5492,13 @@ package android.app.admin {
method public int getStorageEncryptionStatus();
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
+ method public boolean hasUserSetupCompleted();
method public boolean installCaCert(android.content.ComponentName, byte[]);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
+ method public boolean isDeviceInitializerApp(java.lang.String);
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5510,6 +5515,7 @@ package android.app.admin {
method public void setAutoTimeRequired(android.content.ComponentName, boolean);
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+ method public boolean setDeviceInitializer(android.content.ComponentName, android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
@@ -5536,6 +5542,7 @@ package android.app.admin {
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
+ method public boolean setUserEnabled(android.content.ComponentName);
method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
method public void uninstallAllUserCaCerts(android.content.ComponentName);
method public void uninstallCaCert(android.content.ComponentName, byte[]);
@@ -5557,6 +5564,10 @@ package android.app.admin {
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION";
field public static final java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
diff --git a/api/system-current.txt b/api/system-current.txt
index 2ffc4e1..8a9259f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5523,6 +5523,7 @@ package android.app.admin {
method public void onPasswordFailed(android.content.Context, android.content.Intent);
method public void onPasswordSucceeded(android.content.Context, android.content.Intent);
method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent);
+ method public void onReadyForUserInitialization(android.content.Context, android.content.Intent);
method public void onReceive(android.content.Context, android.content.Intent);
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
@@ -5534,6 +5535,7 @@ package android.app.admin {
field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED";
field public static final java.lang.String ACTION_PASSWORD_SUCCEEDED = "android.app.action.ACTION_PASSWORD_SUCCEEDED";
field public static final java.lang.String ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.PROFILE_PROVISIONING_COMPLETE";
+ field public static final java.lang.String ACTION_READY_FOR_USER_INITIALIZATION = "android.app.action.READY_FOR_USER_INITIALIZATION";
field public static final java.lang.String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE";
@@ -5545,6 +5547,7 @@ package android.app.admin {
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
method public void addUserRestriction(android.content.ComponentName, java.lang.String);
method public void clearCrossProfileIntentFilters(android.content.ComponentName);
+ method public void clearDeviceInitializerApp();
method public void clearDeviceOwnerApp(java.lang.String);
method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
method public void clearProfileOwner(android.content.ComponentName);
@@ -5561,6 +5564,7 @@ package android.app.admin {
method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
+ method public java.lang.String getDeviceInitializerApp();
method public java.lang.String getDeviceOwner();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
@@ -5589,11 +5593,13 @@ package android.app.admin {
method public int getStorageEncryptionStatus();
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
+ method public boolean hasUserSetupCompleted();
method public boolean installCaCert(android.content.ComponentName, byte[]);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
+ method public boolean isDeviceInitializerApp(java.lang.String);
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5611,6 +5617,7 @@ package android.app.admin {
method public void setAutoTimeRequired(android.content.ComponentName, boolean);
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+ method public boolean setDeviceInitializer(android.content.ComponentName, android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
@@ -5637,6 +5644,7 @@ package android.app.admin {
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
+ method public boolean setUserEnabled(android.content.ComponentName);
method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
method public void uninstallAllUserCaCerts(android.content.ComponentName);
method public void uninstallCaCert(android.content.ComponentName, byte[]);
@@ -5660,6 +5668,10 @@ package android.app.admin {
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION";
field public static final java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 4fc990e..6d4a0ff 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -215,7 +215,8 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* <p>A device admin application which listens to this intent can find out if the device was
* provisioned for the device owner or profile owner case by calling respectively
* {@link android.app.admin.DevicePolicyManager#isDeviceOwnerApp} and
- * {@link android.app.admin.DevicePolicyManager#isProfileOwnerApp}.
+ * {@link android.app.admin.DevicePolicyManager#isProfileOwnerApp}. You will generally handle
+ * this in {@link DeviceAdminReceiver#onProfileProvisioningComplete}.
*
* <p>Input: Nothing.</p>
* <p>Output: Nothing</p>
@@ -224,6 +225,23 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
public static final String ACTION_PROFILE_PROVISIONING_COMPLETE =
"android.app.action.PROFILE_PROVISIONING_COMPLETE";
+ /**
+ * Broadcast Action: This broadcast is sent to indicate that the system is ready for the device
+ * initializer to perform user setup tasks. This is only applicable to devices managed by a
+ * device owner app.
+ *
+ * <p>The broadcast will be limited to the {@link DeviceAdminReceiver} component specified in
+ * the (@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME) field
+ * of the original intent or NFC bump that started the provisioning process. You will generally
+ * handle this in {@link DeviceAdminReceiver#onReadyForUserInitialization}.
+ *
+ * <p>Input: Nothing.</p>
+ * <p>Output: Nothing</p>
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_READY_FOR_USER_INITIALIZATION =
+ "android.app.action.READY_FOR_USER_INITIALIZATION";
+
/** @hide */
public static final String ACTION_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS";
@@ -245,7 +263,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
/** @hide */
public static final String EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE = "android.app.extra.CHOOSE_PRIVATE_KEY_RESPONSE";
- /**
+ /**
* Name under which a DevicePolicy component publishes information
* about itself. This meta-data must reference an XML resource containing
* a device-admin tag.
@@ -382,20 +400,20 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
/**
* Called when provisioning of a managed profile or managed device has completed successfully.
*
- * <p> As a prerequisit for the execution of this callback the (@link DeviceAdminReceiver} has
+ * <p> As a prerequisite for the execution of this callback the {@link DeviceAdminReceiver} has
* to declare an intent filter for {@link #ACTION_PROFILE_PROVISIONING_COMPLETE}.
* Its component must also be specified in the {@link DevicePolicyManager#EXTRA_DEVICE_ADMIN}
* of the {@link DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE} intent that started the
* managed provisioning.
*
- * <p>When provisioning is complete, the managed profile is hidden until the profile owner
- * calls {DevicePolicyManager#setProfileEnabled(ComponentName admin)}. Typically a profile
- * owner will enable the profile when it has finished any additional setup such as adding an
- * account by using the {@link AccountManager} and calling apis to bring the profile into the
- * desired state.
+ * <p>When provisioning of a managed profile is complete, the managed profile is hidden until
+ * the profile owner calls {DevicePolicyManager#setProfileEnabled(ComponentName admin)}.
+ * Typically a profile owner will enable the profile when it has finished any additional setup
+ * such as adding an account by using the {@link AccountManager} and calling apis to bring the
+ * profile into the desired state.
*
* <p> Note that provisioning completes without waiting for any server interactions, so the
- * profile owner needs to wait for data to be available if required (e.g android device ids or
+ * profile owner needs to wait for data to be available if required (e.g. android device ids or
* other data that is set as a result of server interactions).
*
* @param context The running context as per {@link #onReceive}.
@@ -405,6 +423,30 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
}
/**
+ * Called during provisioning of a managed device to allow the device initializer to perform
+ * user setup steps. Only device initializers should override this method.
+ *
+ * <p> Called when the DeviceAdminReceiver receives a
+ * {@link #ACTION_READY_FOR_USER_INITIALIZATION} broadcast. As a prerequisite for the execution
+ * of this callback the {@link DeviceAdminReceiver} has
+ * to declare an intent filter for {@link #ACTION_READY_FOR_USER_INITIALIZATION}. Only the
+ * component specified in the
+ * {@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME} field of the
+ * original intent or NFC bump that started the provisioning process will receive this callback.
+ *
+ * <p>It is not assumed that the device initializer is finished when it returns from
+ * this call, as it may do additional setup asynchronously. The device initializer must call
+ * {DevicePolicyManager#setUserEnabled(ComponentName admin)} when it has finished any additional
+ * setup (such as adding an account by using the {@link AccountManager}) in order for the user
+ * to be functional.
+ *
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ */
+ public void onReadyForUserInitialization(Context context, Intent intent) {
+ }
+
+ /**
* Called when a device is entering lock task mode by a package authorized
* by {@link DevicePolicyManager#isLockTaskPermitted(String)}
*
@@ -488,6 +530,8 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
onLockTaskModeEntering(context, intent, pkg);
} else if (ACTION_LOCK_TASK_EXITING.equals(action)) {
onLockTaskModeExiting(context, intent);
+ } else if (ACTION_READY_FOR_USER_INITIALIZATION.equals(action)) {
+ onReadyForUserInitialization(context, intent);
}
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 170224d..cee8e8d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -357,6 +357,52 @@ public class DevicePolicyManager {
"android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
/**
+ * On devices managed by a device owner app, a String representation of a Component name extra
+ * indicating the component of the application that is temporarily granted device owner
+ * privileges during device initialization and profile owner privileges during secondary user
+ * initialization.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ * @see ComponentName#unflattenFromString()
+ */
+ public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME
+ = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME";
+
+ /**
+ * A String extra holding an http url that specifies the download location of the device
+ * initializer package. When not provided it is assumed that the device initializer package is
+ * already installed.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ */
+ public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION
+ = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION";
+
+ /**
+ * A String extra holding a http cookie header which should be used in the http request to the
+ * url specified in {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ */
+ public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER
+ = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
+
+ /**
+ * A String extra holding the SHA-1 checksum of the file at download location specified in
+ * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}. If this doesn't
+ * match the file at the download location an error will be shown to the user and the user will
+ * be asked to factory reset the device.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ */
+ public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM
+ = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM";
+
+ /**
* This MIME type is used for starting the Device Owner provisioning.
*
* <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -2382,6 +2428,112 @@ public class DevicePolicyManager {
}
/**
+ * Sets the given component as the device initializer. The package must already be installed and
+ * set as an active device administrator, and there must not be an existing device initializer,
+ * for this call to succeed. This method can only be called by an app holding the
+ * MANAGE_DEVICE_ADMINS permission before the device is provisioned or by a device owner app. A
+ * device initializer app is granted device owner privileges during device initialization and
+ * profile owner privileges during secondary user initialization.
+ * @param who Which {@link DeviceAdminReceiver} this request is associated with, or null if not
+ * called by the device owner.
+ * @param initializer Which {@link DeviceAdminReceiver} to make device initializer.
+ * @param initializerName The user-visible name of the device initializer.
+ * @return whether the package was successfully registered as the device initializer.
+ * @throws IllegalArgumentException if the package name is null or invalid
+ * @throws IllegalStateException if the caller is not device owner or the device has
+ * already been provisioned or a device initializer already exists.
+ */
+ public boolean setDeviceInitializer(ComponentName who, ComponentName initializer,
+ String initializerName) throws IllegalArgumentException, IllegalStateException {
+ if (mService != null) {
+ try {
+ return mService.setDeviceInitializer(who, initializer, initializerName);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed to set device initializer");
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Used to determine if a particular package has been registered as the device initializer.
+ *
+ * @param packageName the package name of the app, to compare with the registered device
+ * initializer app, if any.
+ * @return whether or not the caller is registered as the device initializer app.
+ */
+ public boolean isDeviceInitializerApp(String packageName) {
+ if (mService != null) {
+ try {
+ return mService.isDeviceInitializer(packageName);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed to check device initializer");
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Clears the current device initializer. The caller must be the device initializer.
+ *
+ * This function should be used cautiously as once it is called it cannot
+ * be undone.
+ */
+ public void clearDeviceInitializerApp() {
+ if (mService != null) {
+ try {
+ mService.clearDeviceInitializer(mContext.getPackageName());
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed to clear device initializer");
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Gets the device initializer of the system.
+ *
+ * @return the package name of the device initializer.
+ */
+ @SystemApi
+ public String getDeviceInitializerApp() {
+ if (mService != null) {
+ try {
+ return mService.getDeviceInitializer();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed to get device initializer");
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the enabled state of the user. A user should be enabled only once it is ready to
+ * be used.
+ *
+ * <p>Device initializer must call this method to mark the user as functional.
+ * Only the device initializer agent can call this.
+ *
+ * <p>When the user is enabled, if the device initializer is not also the device owner, the
+ * device initializer will no longer have elevated permissions to call methods in this class.
+ * Additionally, it will be removed as an active administrator and its
+ * {@link DeviceAdminReceiver} will be disabled.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @return whether the user is now enabled.
+ */
+ public boolean setUserEnabled(ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.setUserEnabled(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return false;
+ }
+
+ /**
* @hide
* @deprecated Use #ACTION_SET_PROFILE_OWNER
* Sets the given component as an active admin and registers the package as the profile
@@ -2434,7 +2586,6 @@ public class DevicePolicyManager {
}
/**
- * @hide
* Checks if the user was already setup.
*/
public boolean hasUserSetupCompleted() {
@@ -3119,8 +3270,7 @@ public class DevicePolicyManager {
}
/**
- * Called by a profile or device owner to set a user restriction specified
- * by the key.
+ * Called by a profile or device owner to set a user restriction specified by the key.
* <p>
* The calling device admin must be a profile or device owner; if it is not,
* a security exception will be thrown.
@@ -3141,8 +3291,7 @@ public class DevicePolicyManager {
}
/**
- * Called by a profile or device owner to clear a user restriction specified
- * by the key.
+ * Called by a profile or device owner to clear a user restriction specified by the key.
* <p>
* The calling device admin must be a profile or device owner; if it is not,
* a security exception will be thrown.
@@ -3163,7 +3312,7 @@ public class DevicePolicyManager {
}
/**
- * Called by device or profile owner to hide or unhide packages. When a package is hidden it
+ * Called by profile or device owners to hide or unhide packages. When a package is hidden it
* is unavailable for use, but the data and actual package file remain.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -3185,7 +3334,7 @@ public class DevicePolicyManager {
}
/**
- * Called by device or profile owner to determine if a package is hidden.
+ * Called by profile or device owners to determine if a package is hidden.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param packageName The name of the package to retrieve the hidden status of.
@@ -3203,7 +3352,7 @@ public class DevicePolicyManager {
}
/**
- * Called by profile or device owner to re-enable a system app that was disabled by default
+ * Called by profile or device owners to re-enable a system app that was disabled by default
* when the user was initialized.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -3220,7 +3369,7 @@ public class DevicePolicyManager {
}
/**
- * Called by profile or device owner to re-enable system apps by intent that were disabled
+ * Called by profile or device owners to re-enable system apps by intent that were disabled
* by default when the user was initialized.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 714e740..5e58fe0 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -199,4 +199,10 @@ interface IDevicePolicyManager {
boolean getAutoTimeRequired();
boolean isRemovingAdmin(in ComponentName adminReceiver, int userHandle);
+
+ boolean setUserEnabled(in ComponentName who);
+ boolean isDeviceInitializer(String packageName);
+ void clearDeviceInitializer(String packageName);
+ boolean setDeviceInitializer(in ComponentName who, in ComponentName initializer, String initializerName);
+ String getDeviceInitializer();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
index 57ed876..ea59d4b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
@@ -53,6 +53,7 @@ class DeviceOwner {
private static final String DEVICE_OWNER_XML = "device_owner.xml";
private static final String TAG_DEVICE_OWNER = "device-owner";
+ private static final String TAG_DEVICE_INITIALIZER = "device-initializer";
private static final String TAG_PROFILE_OWNER = "profile-owner";
private static final String ATTR_NAME = "name";
private static final String ATTR_PACKAGE = "package";
@@ -68,6 +69,9 @@ class DeviceOwner {
// Internal state for the device owner package.
private OwnerInfo mDeviceOwner;
+ // Internal state for the device initializer package.
+ private OwnerInfo mDeviceInitializer;
+
// Internal state for the profile owner packages.
private final HashMap<Integer, OwnerInfo> mProfileOwners = new HashMap<Integer, OwnerInfo>();
@@ -104,6 +108,15 @@ class DeviceOwner {
}
/**
+ * Creates an instance of the device owner object with the device initializer set.
+ */
+ static DeviceOwner createWithDeviceInitializer(String packageName, String ownerName) {
+ DeviceOwner owner = new DeviceOwner();
+ owner.mDeviceInitializer = new OwnerInfo(ownerName, packageName);
+ return owner;
+ }
+
+ /**
* Creates an instance of the device owner object with the profile owner set.
*/
static DeviceOwner createWithProfileOwner(ComponentName admin, String ownerName, int userId) {
@@ -128,6 +141,26 @@ class DeviceOwner {
mDeviceOwner = null;
}
+ String getDeviceInitializerPackageName() {
+ return mDeviceInitializer != null ? mDeviceInitializer.packageName : null;
+ }
+
+ String getDeviceInitializerName() {
+ return mDeviceInitializer != null ? mDeviceInitializer.name : null;
+ }
+
+ void setDeviceInitializer(String packageName, String ownerName) {
+ mDeviceInitializer = new OwnerInfo(ownerName, packageName);
+ }
+
+ void clearDeviceInitializer() {
+ mDeviceInitializer = null;
+ }
+
+ boolean hasDeviceInitializer() {
+ return mDeviceInitializer != null;
+ }
+
void setProfileOwner(ComponentName admin, String ownerName, int userId) {
mProfileOwners.put(userId, new OwnerInfo(ownerName, admin));
}
@@ -199,6 +232,10 @@ class DeviceOwner {
String name = parser.getAttributeValue(null, ATTR_NAME);
String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
mDeviceOwner = new OwnerInfo(name, packageName);
+ } else if (tag.equals(TAG_DEVICE_INITIALIZER)) {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+ mDeviceInitializer = new OwnerInfo(name, packageName);
} else if (tag.equals(TAG_PROFILE_OWNER)) {
String profileOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE);
String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME);
@@ -259,6 +296,16 @@ class DeviceOwner {
out.endTag(null, TAG_DEVICE_OWNER);
}
+ // Write device initializer tag
+ if (mDeviceInitializer != null) {
+ out.startTag(null, TAG_DEVICE_INITIALIZER);
+ out.attribute(null, ATTR_PACKAGE, mDeviceInitializer.packageName);
+ if (mDeviceInitializer.name != null) {
+ out.attribute(null, ATTR_NAME, mDeviceInitializer.name);
+ }
+ out.endTag(null, TAG_DEVICE_INITIALIZER);
+ }
+
// Write profile owner tags
if (mProfileOwners.size() > 0) {
for (HashMap.Entry<Integer, OwnerInfo> owner : mProfileOwners.entrySet()) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a19cd9f..6270212 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1139,13 +1139,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
boolean ownsProfile = (getProfileOwner(userHandle) != null
&& getProfileOwner(userHandle).getPackageName()
.equals(admin.info.getPackageName()));
+ boolean ownsInitialization = isDeviceInitializer(admin.info.getPackageName())
+ && !hasUserSetupCompleted(userHandle);
if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
- if (ownsDevice) {
+ if (ownsDevice || (userHandle == UserHandle.USER_OWNER && ownsInitialization)) {
return admin;
}
} else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
- if (ownsDevice || ownsProfile) {
+ if (ownsDevice || ownsProfile || ownsInitialization) {
return admin;
}
} else {
@@ -1899,7 +1901,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
if (admin.getUid() != Binder.getCallingUid()) {
- // If trying to remove device owner, refuse when the caller is not the owner.
+ // Active device owners must remain active admins.
if (isDeviceOwner(adminReceiver.getPackageName())) {
return;
}
@@ -3866,6 +3868,112 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
+ public boolean setDeviceInitializer(ComponentName who, ComponentName initializer,
+ String ownerName) {
+ if (!mHasFeature) {
+ return false;
+ }
+ if (initializer == null || !DeviceOwner.isInstalled(
+ initializer.getPackageName(), mContext.getPackageManager())) {
+ throw new IllegalArgumentException("Invalid component name " + initializer
+ + " for device initializer");
+ }
+ synchronized (this) {
+ enforceCanSetDeviceInitializer(who);
+
+ if (mDeviceOwner != null && mDeviceOwner.hasDeviceInitializer()) {
+ throw new IllegalStateException(
+ "Trying to set device initializer but device initializer is already set.");
+ }
+
+ if (mDeviceOwner == null) {
+ // Device owner state does not exist, create it.
+ mDeviceOwner = DeviceOwner.createWithDeviceInitializer(
+ initializer.getPackageName(), ownerName);
+ mDeviceOwner.writeOwnerFile();
+ return true;
+ } else {
+ // Device owner already exists, update it.
+ mDeviceOwner.setDeviceInitializer(initializer.getPackageName(), ownerName);
+ mDeviceOwner.writeOwnerFile();
+ return true;
+ }
+ }
+ }
+
+ private void enforceCanSetDeviceInitializer(ComponentName who) {
+ if (who == null) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
+ if (hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+ throw new IllegalStateException(
+ "Trying to set device initializer but device is already provisioned.");
+ }
+ } else {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+ }
+
+ @Override
+ public boolean isDeviceInitializer(String packageName) {
+ if (!mHasFeature) {
+ return false;
+ }
+ synchronized (this) {
+ return mDeviceOwner != null
+ && mDeviceOwner.hasDeviceInitializer()
+ && mDeviceOwner.getDeviceInitializerPackageName().equals(packageName);
+ }
+ }
+
+ @Override
+ public String getDeviceInitializer() {
+ if (!mHasFeature) {
+ return null;
+ }
+ synchronized (this) {
+ if (mDeviceOwner != null && mDeviceOwner.hasDeviceInitializer()) {
+ return mDeviceOwner.getDeviceInitializerPackageName();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void clearDeviceInitializer(String packageName) {
+ if (!mHasFeature) {
+ return;
+ }
+ if (packageName == null) {
+ throw new NullPointerException("packageName is null");
+ }
+ try {
+ int uid = mContext.getPackageManager().getPackageUid(packageName, 0);
+ if (uid != Binder.getCallingUid()) {
+ throw new SecurityException(
+ "clearDeviceInitializer can only be called by the device initializer");
+ }
+ } catch (NameNotFoundException e) {
+ throw new SecurityException(e);
+ }
+ if (!isDeviceInitializer(packageName)) {
+ throw new SecurityException(
+ "clearDeviceInitializer can only be called by the device initializer");
+ }
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (mDeviceOwner != null) {
+ mDeviceOwner.clearDeviceInitializer();
+ mDeviceOwner.writeOwnerFile();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @Override
public boolean setProfileOwner(ComponentName who, String ownerName, int userHandle) {
if (!mHasFeature) {
return false;
@@ -3960,6 +4068,51 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
+ public boolean setUserEnabled(ComponentName who) {
+ if (!mHasFeature) {
+ return false;
+ }
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ int userId = UserHandle.getCallingUserId();
+
+ ActiveAdmin activeAdmin =
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (!isDeviceInitializer(activeAdmin.info.getPackageName())) {
+ throw new SecurityException(
+ "This method can only be called by device initializers");
+ }
+
+ long id = Binder.clearCallingIdentity();
+ try {
+ if (!isDeviceOwner(activeAdmin.info.getPackageName())) {
+ IPackageManager ipm = AppGlobals.getPackageManager();
+ ipm.setComponentEnabledSetting(who,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP, userId);
+
+ removeActiveAdmin(who, userId);
+ }
+
+ if (userId == UserHandle.USER_OWNER) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 1);
+ }
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 1, userId);
+ } catch (RemoteException e) {
+ Log.i(LOG_TAG, "Can't talk to package manager", e);
+ return false;
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ return true;
+ }
+ }
+
+ @Override
public void setProfileEnabled(ComponentName who) {
if (!mHasFeature) {
return;