From 20118f18c11c01c7743b5646cc1a0039c2e90037 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Wed, 11 Feb 2015 12:34:08 -0500 Subject: 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 --- .../android/server/devicepolicy/DeviceOwner.java | 47 ++++++ .../devicepolicy/DevicePolicyManagerService.java | 159 ++++++++++++++++++++- 2 files changed, 203 insertions(+), 3 deletions(-) (limited to 'services/devicepolicy') 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 mProfileOwners = new HashMap(); @@ -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 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; -- cgit v1.1