diff options
author | Adam Connors <adamconnors@google.com> | 2014-01-09 10:42:56 +0000 |
---|---|---|
committer | Adam Connors <adamconnors@google.com> | 2014-02-06 10:07:19 +0000 |
commit | 776c555d954d9494069f786785877c08add27327 (patch) | |
tree | 1cad8070340a8e2924efd9ca371d6c5098cfcfdc /services/devicepolicy | |
parent | cb1b23b5e600abe542e3374b66c2be7976fccd72 (diff) | |
download | frameworks_base-776c555d954d9494069f786785877c08add27327.zip frameworks_base-776c555d954d9494069f786785877c08add27327.tar.gz frameworks_base-776c555d954d9494069f786785877c08add27327.tar.bz2 |
Extend DeviceOwner concept to accommodate ProfileOwners
ProfileOwners, like DeviceOwners, are Device Admins that have
additional priviledges. ProfileOwners however are scoped per
user.
Change-Id: I1e22c85878e0672121e6ebbe97fca38591f992b2
Diffstat (limited to 'services/devicepolicy')
-rw-r--r-- | services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java | 274 | ||||
-rw-r--r-- | services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java | 215 |
2 files changed, 371 insertions, 118 deletions
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java new file mode 100644 index 0000000..1b048a1 --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.devicepolicy; + +import android.app.AppGlobals; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Environment; +import android.os.RemoteException; +import android.util.AtomicFile; +import android.util.Slog; +import android.util.Xml; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FastXmlSerializer; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; + +/** + * Stores and restores state for the Device and Profile owners. By definition there can be + * only one device owner, but there may be a profile owner for each user. + */ +public class DeviceOwner { + private static final String TAG = "DevicePolicyManagerService"; + + private static final String DEVICE_OWNER_XML = "device_owner.xml"; + private static final String TAG_DEVICE_OWNER = "device-owner"; + private static final String TAG_PROFILE_OWNER = "profile-owner"; + private static final String ATTR_NAME = "name"; + private static final String ATTR_PACKAGE = "package"; + private static final String ATTR_USERID = "userId"; + + private AtomicFile fileForWriting; + + // Input/Output streams for testing. + private InputStream mInputStreamForTest; + private OutputStream mOutputStreamForTest; + + // Internal state for the device owner package. + private String mDeviceOwnerPackageName; + private String mDeviceOwnerName; + + // Internal state for the profile owner packages. + private final HashMap<Integer, String[]> mProfileOwners = new HashMap<Integer, String[]>(); + + // Private default constructor. + private DeviceOwner() { + } + + @VisibleForTesting + DeviceOwner(InputStream in, OutputStream out) { + mInputStreamForTest = in; + mOutputStreamForTest = out; + } + + /** + * Loads the device owner state from disk. + */ + static DeviceOwner load() { + DeviceOwner owner = new DeviceOwner(); + if (new File(Environment.getSystemSecureDirectory(), DEVICE_OWNER_XML).exists()) { + owner.readOwnerFile(); + return owner; + } else { + return null; + } + } + + /** + * Creates an instance of the device owner object with the device owner set. + */ + static DeviceOwner createWithDeviceOwner(String packageName, String ownerName) { + DeviceOwner owner = new DeviceOwner(); + owner.mDeviceOwnerPackageName = packageName; + owner.mDeviceOwnerName = ownerName; + return owner; + } + + /** + * Creates an instance of the device owner object with the profile owner set. + */ + static DeviceOwner createWithProfileOwner(String packageName, String ownerName, int userId) { + DeviceOwner owner = new DeviceOwner(); + owner.mProfileOwners.put(userId, new String[] { packageName, ownerName }); + return owner; + } + + String getDeviceOwnerPackageName() { + return mDeviceOwnerPackageName; + } + + String getDeviceOwnerName() { + return mDeviceOwnerName; + } + + void setDeviceOwner(String packageName, String ownerName) { + mDeviceOwnerPackageName = packageName; + mDeviceOwnerName = ownerName; + } + + void setProfileOwner(String packageName, String ownerName, int userId) { + mProfileOwners.put(userId, new String[] { packageName, ownerName }); + } + + void removeProfileOwner(int userId) { + mProfileOwners.remove(userId); + } + + String getProfileOwnerPackageName(int userId) { + String[] profileOwner = mProfileOwners.get(userId); + return profileOwner != null ? profileOwner[0] : null; + } + + String getProfileOwnerName(int userId) { + String[] profileOwner = mProfileOwners.get(userId); + return profileOwner != null ? profileOwner[1] : null; + } + + boolean hasDeviceOwner() { + return mDeviceOwnerPackageName != null; + } + + static boolean isInstalled(String packageName, PackageManager pm) { + try { + PackageInfo pi; + if ((pi = pm.getPackageInfo(packageName, 0)) != null) { + if ((pi.applicationInfo.flags) != 0) { + return true; + } + } + } catch (NameNotFoundException nnfe) { + Slog.w(TAG, "Device Owner package " + packageName + " not installed."); + } + return false; + } + + static boolean isInstalledForUser(String packageName, int userHandle) { + try { + PackageInfo pi = (AppGlobals.getPackageManager()) + .getPackageInfo(packageName, 0, userHandle); + if (pi != null && pi.applicationInfo.flags != 0) { + return true; + } + } catch (RemoteException re) { + throw new RuntimeException("Package manager has died", re); + } + + return false; + } + + void readOwnerFile() { + try { + InputStream input = openRead(); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(input, null); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT) { + if (type!=XmlPullParser.START_TAG) { + continue; + } + + String tag = parser.getName(); + if (tag.equals(TAG_DEVICE_OWNER)) { + mDeviceOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE); + mDeviceOwnerName = parser.getAttributeValue(null, ATTR_NAME); + } else if (tag.equals(TAG_PROFILE_OWNER)) { + String profileOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE); + String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME); + int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USERID)); + mProfileOwners.put(userId, + new String[] { profileOwnerPackageName, profileOwnerName }); + } else { + throw new XmlPullParserException( + "Unexpected tag in device owner file: " + tag); + } + } + input.close(); + } catch (XmlPullParserException xppe) { + Slog.e(TAG, "Error parsing device-owner file\n" + xppe); + } catch (IOException ioe) { + Slog.e(TAG, "IO Exception when reading device-owner file\n" + ioe); + } + } + + void writeOwnerFile() { + synchronized (this) { + writeOwnerFileLocked(); + } + } + + private void writeOwnerFileLocked() { + try { + OutputStream outputStream = startWrite(); + XmlSerializer out = new FastXmlSerializer(); + out.setOutput(outputStream, "utf-8"); + out.startDocument(null, true); + + // Write device owner tag + if (mDeviceOwnerPackageName != null) { + out.startTag(null, TAG_DEVICE_OWNER); + out.attribute(null, ATTR_PACKAGE, mDeviceOwnerPackageName); + if (mDeviceOwnerName != null) { + out.attribute(null, ATTR_NAME, mDeviceOwnerName); + } + out.endTag(null, TAG_DEVICE_OWNER); + } + + // Write profile owner tags + if (mProfileOwners.size() > 0) { + for (HashMap.Entry<Integer, String[]> owner : mProfileOwners.entrySet()) { + out.startTag(null, TAG_PROFILE_OWNER); + out.attribute(null, ATTR_PACKAGE, owner.getValue()[0]); + out.attribute(null, ATTR_NAME, owner.getValue()[1]); + out.attribute(null, ATTR_USERID, Integer.toString(owner.getKey())); + out.endTag(null, TAG_PROFILE_OWNER); + } + } + out.endDocument(); + out.flush(); + finishWrite(outputStream); + } catch (IOException ioe) { + Slog.e(TAG, "IO Exception when writing device-owner file\n" + ioe); + } + } + + private InputStream openRead() throws IOException { + if (mInputStreamForTest != null) { + return mInputStreamForTest; + } + + return new AtomicFile(new File(Environment.getSystemSecureDirectory(), + DEVICE_OWNER_XML)).openRead(); + } + + private OutputStream startWrite() throws IOException { + if (mOutputStreamForTest != null) { + return mOutputStreamForTest; + } + + fileForWriting = new AtomicFile(new File(Environment.getSystemSecureDirectory(), + DEVICE_OWNER_XML)); + return fileForWriting.startWrite(); + } + + private void finishWrite(OutputStream stream) { + if (fileForWriting != null) { + fileForWriting.finishWrite((FileOutputStream) stream); + } + } +}
\ No newline at end of file diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 186fbe1..a8f2df1 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -26,10 +26,6 @@ import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; import com.android.org.conscrypt.TrustedCertificateStore; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - import android.app.Activity; import android.app.ActivityManagerNative; import android.app.AlarmManager; @@ -49,9 +45,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.net.ProxyProperties; @@ -75,7 +69,6 @@ import android.security.Credentials; import android.security.IKeyChainService; import android.security.KeyChain; import android.security.KeyChain.KeyChainConnection; -import android.util.AtomicFile; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; @@ -84,6 +77,10 @@ import android.util.SparseArray; import android.util.Xml; import android.view.IWindowManager; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; @@ -132,6 +129,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { IWindowManager mIWindowManager; NotificationManager mNotificationManager; + // Stores and loads state on device and profile owners. private DeviceOwner mDeviceOwner; /** @@ -601,6 +599,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Slog.w(LOG_TAG, "Tried to remove device policy file for user 0! Ignoring."); return; } + + mDeviceOwner.removeProfileOwner(userHandle); + mDeviceOwner.writeOwnerFile(); + DevicePolicyData policy = mUserData.get(userHandle); if (policy != null) { mUserData.remove(userHandle); @@ -614,9 +616,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { void loadDeviceOwner() { synchronized (this) { - if (DeviceOwner.isRegistered()) { - mDeviceOwner = new DeviceOwner(); - } + mDeviceOwner = DeviceOwner.load(); } } @@ -1294,7 +1294,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (admin.getUid() != Binder.getCallingUid()) { // If trying to remove device owner, refuse when the caller is not the owner. if (mDeviceOwner != null - && adminReceiver.getPackageName().equals(mDeviceOwner.getPackageName())) { + && adminReceiver.getPackageName().equals( + mDeviceOwner.getDeviceOwnerPackageName())) { return; } mContext.enforceCallingOrSelfPermission( @@ -2739,14 +2740,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + " for device owner"); } synchronized (this) { - if (mDeviceOwner == null && !isDeviceProvisioned()) { - mDeviceOwner = new DeviceOwner(packageName, ownerName); + if (isDeviceProvisioned()) { + throw new IllegalStateException( + "Trying to set device owner but device is already provisioned."); + } + + if (mDeviceOwner != null && mDeviceOwner.hasDeviceOwner()) { + throw new IllegalStateException( + "Trying to set device owner but device owner is already set."); + } + + if (mDeviceOwner == null) { + // Device owner is not set and does not exist, set it. + mDeviceOwner = DeviceOwner.createWithDeviceOwner(packageName, ownerName); mDeviceOwner.writeOwnerFile(); return true; } else { - throw new IllegalStateException("Trying to set device owner to " + packageName - + ", owner=" + mDeviceOwner.getPackageName() - + ", device_provisioned=" + isDeviceProvisioned()); + // Device owner is not set but a profile owner exists, update Device owner state. + mDeviceOwner.setDeviceOwner(packageName, ownerName); + mDeviceOwner.writeOwnerFile(); + return true; } } } @@ -2758,7 +2771,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } synchronized (this) { return mDeviceOwner != null - && mDeviceOwner.getPackageName().equals(packageName); + && mDeviceOwner.getDeviceOwnerPackageName().equals(packageName); } } @@ -2769,7 +2782,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } synchronized (this) { if (mDeviceOwner != null) { - return mDeviceOwner.getPackageName(); + return mDeviceOwner.getDeviceOwnerPackageName(); } } return null; @@ -2783,7 +2796,67 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); synchronized (this) { if (mDeviceOwner != null) { - return mDeviceOwner.getName(); + return mDeviceOwner.getDeviceOwnerName(); + } + } + return null; + } + + @Override + public boolean setProfileOwner(String packageName, String ownerName, int userHandle) { + if (!mHasFeature) { + return false; + } + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); + if (packageName == null + || !DeviceOwner.isInstalledForUser(packageName, userHandle)) { + throw new IllegalArgumentException("Package name " + packageName + + " not installed for userId:" + userHandle); + } + synchronized (this) { + if (isUserSetupComplete(userHandle)) { + throw new IllegalStateException( + "Trying to set profile owner but user is already set-up."); + } + if (mDeviceOwner == null) { + // Device owner state does not exist, create it. + mDeviceOwner = DeviceOwner.createWithProfileOwner(packageName, ownerName, + userHandle); + mDeviceOwner.writeOwnerFile(); + return true; + } else { + // Device owner already exists, update it. + mDeviceOwner.setProfileOwner(packageName, ownerName, userHandle); + mDeviceOwner.writeOwnerFile(); + return true; + } + } + } + + @Override + public String getProfileOwner(int userHandle) { + if (!mHasFeature) { + return null; + } + + synchronized (this) { + if (mDeviceOwner != null) { + return mDeviceOwner.getProfileOwnerPackageName(userHandle); + } + } + return null; + } + + @Override + public String getProfileOwnerName(int userHandle) { + if (!mHasFeature) { + return null; + } + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); + + synchronized (this) { + if (mDeviceOwner != null) { + return mDeviceOwner.getProfileOwnerName(userHandle); } } return null; @@ -2794,6 +2867,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Settings.Global.DEVICE_PROVISIONED, 0) > 0; } + private boolean isUserSetupComplete(int userId) { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, 0, userId) > 0; + } + private void enforceCrossUserPermission(int userHandle) { if (userHandle < 0) { throw new IllegalArgumentException("Invalid userId " + userHandle); @@ -2858,103 +2936,4 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } } - - static 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 ATTR_NAME = "name"; - private static final String ATTR_PACKAGE = "package"; - private String mPackageName; - private String mOwnerName; - - DeviceOwner() { - readOwnerFile(); - } - - DeviceOwner(String packageName, String ownerName) { - this.mPackageName = packageName; - this.mOwnerName = ownerName; - } - - static boolean isRegistered() { - return new File(Environment.getSystemSecureDirectory(), - DEVICE_OWNER_XML).exists(); - } - - String getPackageName() { - return mPackageName; - } - - String getName() { - return mOwnerName; - } - - static boolean isInstalled(String packageName, PackageManager pm) { - try { - PackageInfo pi; - if ((pi = pm.getPackageInfo(packageName, 0)) != null) { - if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - return true; - } - } - } catch (NameNotFoundException nnfe) { - Slog.w(LOG_TAG, "Device Owner package " + packageName + " not installed."); - } - return false; - } - - void readOwnerFile() { - AtomicFile file = new AtomicFile(new File(Environment.getSystemSecureDirectory(), - DEVICE_OWNER_XML)); - try { - FileInputStream input = file.openRead(); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(input, null); - int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && type != XmlPullParser.START_TAG) { - } - String tag = parser.getName(); - if (!TAG_DEVICE_OWNER.equals(tag)) { - throw new XmlPullParserException( - "Device Owner file does not start with device-owner tag: found " + tag); - } - mPackageName = parser.getAttributeValue(null, ATTR_PACKAGE); - mOwnerName = parser.getAttributeValue(null, ATTR_NAME); - input.close(); - } catch (XmlPullParserException xppe) { - Slog.e(LOG_TAG, "Error parsing device-owner file\n" + xppe); - } catch (IOException ioe) { - Slog.e(LOG_TAG, "IO Exception when reading device-owner file\n" + ioe); - } - } - - void writeOwnerFile() { - synchronized (this) { - writeOwnerFileLocked(); - } - } - - private void writeOwnerFileLocked() { - AtomicFile file = new AtomicFile(new File(Environment.getSystemSecureDirectory(), - DEVICE_OWNER_XML)); - try { - FileOutputStream output = file.startWrite(); - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(output, "utf-8"); - out.startDocument(null, true); - out.startTag(null, TAG_DEVICE_OWNER); - out.attribute(null, ATTR_PACKAGE, mPackageName); - if (mOwnerName != null) { - out.attribute(null, ATTR_NAME, mOwnerName); - } - out.endTag(null, TAG_DEVICE_OWNER); - out.endDocument(); - out.flush(); - file.finishWrite(output); - } catch (IOException ioe) { - Slog.e(LOG_TAG, "IO Exception when writing device-owner file\n" + ioe); - } - } - } } |