diff options
-rw-r--r-- | api/current.txt | 11 | ||||
-rw-r--r-- | api/system-current.txt | 12 | ||||
-rw-r--r-- | core/java/android/app/admin/DeviceAdminReceiver.java | 62 | ||||
-rw-r--r-- | core/java/android/app/admin/DevicePolicyManager.java | 167 | ||||
-rw-r--r-- | core/java/android/app/admin/IDevicePolicyManager.aidl | 6 | ||||
-rw-r--r-- | services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java | 47 | ||||
-rw-r--r-- | services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java | 159 |
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; |