summaryrefslogtreecommitdiffstats
path: root/services/devicepolicy
diff options
context:
space:
mode:
authorAdam Connors <adamconnors@google.com>2014-01-09 10:42:56 +0000
committerAdam Connors <adamconnors@google.com>2014-02-06 10:07:19 +0000
commit776c555d954d9494069f786785877c08add27327 (patch)
tree1cad8070340a8e2924efd9ca371d6c5098cfcfdc /services/devicepolicy
parentcb1b23b5e600abe542e3374b66c2be7976fccd72 (diff)
downloadframeworks_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.java274
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java215
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);
- }
- }
- }
}