Merge "Starting point for User Restrictions API"
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d80598c..c507245 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -666,6 +666,15 @@
public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
/**
+ * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
+ * if the system failed to install the package because the user is restricted from installing
+ * apps.
+ * @hide
+ */
+ public static final int INSTALL_FAILED_USER_RESTRICTED = -111;
+
+ /**
* Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the
* package's data directory.
*
@@ -710,6 +719,15 @@
public static final int DELETE_FAILED_DEVICE_POLICY_MANAGER = -2;
/**
+ * Deletion failed return code: this is passed to the
+ * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
+ * failed to delete the package since the user is restricted.
+ *
+ * @hide
+ */
+ public static final int DELETE_FAILED_USER_RESTRICTED = -3;
+
+ /**
* Return code that is passed to the {@link IPackageMoveObserver} by
* {@link #movePackage(android.net.Uri, IPackageMoveObserver)} when the
* package has been successfully moved by the system.
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index ec02ae0..34c9740 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -17,6 +17,7 @@
package android.os;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
@@ -37,4 +38,6 @@
void wipeUser(int userHandle);
int getUserSerialNumber(int userHandle);
int getUserHandle(int userSerialNumber);
+ Bundle getUserRestrictions(int userHandle);
+ void setUserRestrictions(in Bundle restrictions, int userHandle);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d73f99a..e4a5e7f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -35,6 +35,42 @@
private final IUserManager mService;
private final Context mContext;
+ /**
+ * @hide
+ * Key for user restrictions. Specifies if a user is allowed to add or remove accounts.
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String ALLOW_MODIFY_ACCOUNTS = "modify_accounts";
+
+ /**
+ * @hide
+ * Key for user restrictions. Specifies if a user is allowed to change Wi-Fi access points.
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String ALLOW_CONFIG_WIFI = "config_wifi";
+
+ /**
+ * @hide
+ * Key for user restrictions. Specifies if a user is allowed to install applications.
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String ALLOW_INSTALL_APPS = "install_apps";
+
+ /**
+ * @hide
+ * Key for user restrictions. Specifies if a user is allowed to uninstall applications.
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String ALLOW_UNINSTALL_APPS = "uninstall_apps";
+
/** @hide */
public UserManager(Context context, IUserManager service) {
mService = service;
@@ -132,6 +168,35 @@
}
}
+ /** @hide */
+ public Bundle getUserRestrictions() {
+ return getUserRestrictions(Process.myUserHandle());
+ }
+
+ /** @hide */
+ public Bundle getUserRestrictions(UserHandle userHandle) {
+ try {
+ return mService.getUserRestrictions(userHandle.getIdentifier());
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get user restrictions", re);
+ return Bundle.EMPTY;
+ }
+ }
+
+ /** @hide */
+ public void setUserRestrictions(Bundle restrictions) {
+ setUserRestrictions(restrictions, Process.myUserHandle());
+ }
+
+ /** @hide */
+ public void setUserRestrictions(Bundle restrictions, UserHandle userHandle) {
+ try {
+ mService.setUserRestrictions(restrictions, userHandle.getIdentifier());
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not set user restrictions", re);
+ }
+ }
+
/**
* Return the serial number for a user. This is a device-unique
* number assigned to that user; if the user is deleted and then a new
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 6a62809..18b4ec1 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -1889,7 +1889,7 @@
mHandler.post(new Runnable() {
public void run() {
try {
- ActivityManagerNative.getDefault().switchUser(0);
+ ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER);
((UserManager) mContext.getSystemService(Context.USER_SERVICE))
.removeUser(userHandle);
} catch (RemoteException re) {
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 88603dc..2a62c17 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -21,6 +21,7 @@
import android.accounts.AccountAndUser;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
+import android.accounts.AccountManagerResponse;
import android.accounts.AuthenticatorDescription;
import android.accounts.GrantCredentialsPermissionActivity;
import android.accounts.IAccountAuthenticator;
@@ -526,6 +527,9 @@
}
if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account);
+ if (!canUserModifyAccounts(Binder.getCallingUid())) {
+ return false;
+ }
UserAccounts accounts = getUserAccountsForCaller();
// fails if the account already exists
@@ -679,6 +683,14 @@
checkManageAccountsPermission();
UserHandle user = Binder.getCallingUserHandle();
UserAccounts accounts = getUserAccountsForCaller();
+ if (!canUserModifyAccounts(Binder.getCallingUid())) {
+ try {
+ response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "User cannot modify accounts");
+ } catch (RemoteException re) {
+ }
+ }
+
long identityToken = clearCallingIdentity();
cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
@@ -2312,6 +2324,17 @@
Manifest.permission.USE_CREDENTIALS);
}
+ private boolean canUserModifyAccounts(int callingUid) {
+ if (callingUid != android.os.Process.myUid()) {
+ Bundle restrictions = getUserManager().getUserRestrictions(
+ new UserHandle(UserHandle.getUserId(callingUid)));
+ if (!restrictions.getBoolean(UserManager.ALLOW_MODIFY_ACCOUNTS)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
throws RemoteException {
final int callingUid = getCallingUid();
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 46d2cca..b480aa0 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -110,6 +110,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.Environment.UserEnvironment;
import android.security.SystemKeyStore;
import android.util.DisplayMetrics;
@@ -5649,6 +5650,14 @@
null);
final int uid = Binder.getCallingUid();
+ if (!isUserAllowed(uid, UserManager.ALLOW_INSTALL_APPS)) {
+ try {
+ observer.packageInstalled("", PackageManager.INSTALL_FAILED_USER_RESTRICTED);
+ } catch (RemoteException re) {
+ }
+ return;
+ }
+
UserHandle user;
if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
@@ -5685,6 +5694,9 @@
PackageSetting pkgSetting;
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
+ if (!isUserAllowed(uid, UserManager.ALLOW_INSTALL_APPS)) {
+ return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
+ }
long callingId = Binder.clearCallingIdentity();
try {
@@ -5715,7 +5727,19 @@
return PackageManager.INSTALL_SUCCEEDED;
}
-
+
+ private boolean isUserAllowed(int callingUid, String restrictionKey) {
+ if (callingUid != android.os.Process.myUid()) {
+ Bundle restrictions = sUserManager.getUserRestrictions(
+ UserHandle.getUserId(callingUid));
+ if (!restrictions.getBoolean(UserManager.ALLOW_INSTALL_APPS)) {
+ Log.w(TAG, "User does not have permission to: " + restrictionKey);
+ return false;
+ }
+ }
+ return true;
+ }
+
@Override
public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
mContext.enforceCallingOrSelfPermission(
@@ -8071,6 +8095,14 @@
android.Manifest.permission.DELETE_PACKAGES, null);
// Queue up an async operation since the package deletion may take a little while.
final int uid = Binder.getCallingUid();
+ if (!isUserAllowed(uid, UserManager.ALLOW_UNINSTALL_APPS)) {
+ try {
+ observer.packageDeleted(packageName, PackageManager.DELETE_FAILED_USER_RESTRICTED);
+ } catch (RemoteException re) {
+ }
+ return;
+ }
+
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index dbfe34d..5760dcd 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -30,6 +30,7 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -81,6 +82,7 @@
private static final String ATTR_USER_VERSION = "version";
private static final String TAG_USERS = "users";
private static final String TAG_USER = "user";
+ private static final String TAG_RESTRICTIONS = "restrictions";
private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
@@ -104,6 +106,7 @@
private final File mBaseUserPath;
private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
+ private final SparseArray<Bundle> mUserRestrictions = new SparseArray<Bundle>();
/**
* Set of user IDs being actively removed. Removed IDs linger in this set
@@ -343,6 +346,26 @@
}
}
+ @Override
+ public Bundle getUserRestrictions(int userId) {
+ // checkManageUsersPermission("getUserRestrictions");
+
+ synchronized (mPackagesLock) {
+ Bundle restrictions = mUserRestrictions.get(userId);
+ return restrictions != null ? restrictions : Bundle.EMPTY;
+ }
+ }
+
+ @Override
+ public void setUserRestrictions(Bundle restrictions, int userId) {
+ checkManageUsersPermission("setUserRestrictions");
+
+ synchronized (mPackagesLock) {
+ mUserRestrictions.get(userId).putAll(restrictions);
+ writeUserLocked(mUsers.get(userId));
+ }
+ }
+
/**
* Check if we've hit the limit of how many users can be created.
*/
@@ -454,7 +477,7 @@
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
String id = parser.getAttributeValue(null, ATTR_ID);
- UserInfo user = readUser(Integer.parseInt(id));
+ UserInfo user = readUserLocked(Integer.parseInt(id));
if (user != null) {
mUsers.put(user.id, user);
@@ -568,6 +591,15 @@
serializer.text(userInfo.name);
serializer.endTag(null, TAG_NAME);
+ Bundle restrictions = mUserRestrictions.get(userInfo.id);
+ if (restrictions != null) {
+ serializer.startTag(null, TAG_RESTRICTIONS);
+ writeBoolean(serializer, restrictions, UserManager.ALLOW_CONFIG_WIFI);
+ writeBoolean(serializer, restrictions, UserManager.ALLOW_MODIFY_ACCOUNTS);
+ writeBoolean(serializer, restrictions, UserManager.ALLOW_INSTALL_APPS);
+ writeBoolean(serializer, restrictions, UserManager.ALLOW_UNINSTALL_APPS);
+ serializer.endTag(null, TAG_RESTRICTIONS);
+ }
serializer.endTag(null, TAG_USER);
serializer.endDocument();
@@ -620,7 +652,7 @@
}
}
- private UserInfo readUser(int id) {
+ private UserInfo readUserLocked(int id) {
int flags = 0;
int serialNumber = id;
String name = null;
@@ -628,6 +660,8 @@
long creationTime = 0L;
long lastLoggedInTime = 0L;
boolean partial = false;
+ Bundle restrictions = new Bundle();
+ initRestrictionsToDefaults(restrictions);
FileInputStream fis = null;
try {
@@ -663,13 +697,23 @@
partial = true;
}
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- }
- if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {
- type = parser.next();
- if (type == XmlPullParser.TEXT) {
- name = parser.getText();
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tag = parser.getName();
+ if (TAG_NAME.equals(tag)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ name = parser.getText();
+ }
+ } else if (TAG_RESTRICTIONS.equals(tag)) {
+ readBoolean(parser, restrictions, UserManager.ALLOW_CONFIG_WIFI);
+ readBoolean(parser, restrictions, UserManager.ALLOW_MODIFY_ACCOUNTS);
+ readBoolean(parser, restrictions, UserManager.ALLOW_INSTALL_APPS);
+ readBoolean(parser, restrictions, UserManager.ALLOW_UNINSTALL_APPS);
}
}
}
@@ -679,6 +723,7 @@
userInfo.creationTime = creationTime;
userInfo.lastLoggedInTime = lastLoggedInTime;
userInfo.partial = partial;
+ mUserRestrictions.append(id, restrictions);
return userInfo;
} catch (IOException ioe) {
@@ -694,6 +739,27 @@
return null;
}
+ private void readBoolean(XmlPullParser parser, Bundle restrictions,
+ String restrictionKey) {
+ String value = parser.getAttributeValue(null, restrictionKey);
+ restrictions.putBoolean(restrictionKey, value == null ? true : Boolean.parseBoolean(value));
+ }
+
+ private void writeBoolean(XmlSerializer xml, Bundle restrictions, String restrictionKey)
+ throws IOException {
+ if (restrictions.containsKey(restrictionKey)) {
+ xml.attribute(null, restrictionKey,
+ Boolean.toString(restrictions.getBoolean(restrictionKey)));
+ }
+ }
+
+ private void initRestrictionsToDefaults(Bundle restrictions) {
+ restrictions.putBoolean(UserManager.ALLOW_CONFIG_WIFI, true);
+ restrictions.putBoolean(UserManager.ALLOW_MODIFY_ACCOUNTS, true);
+ restrictions.putBoolean(UserManager.ALLOW_INSTALL_APPS, true);
+ restrictions.putBoolean(UserManager.ALLOW_UNINSTALL_APPS, true);
+ }
+
private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) {
String valueString = parser.getAttributeValue(null, attr);
if (valueString == null) return defaultValue;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 1758d93..01b65a3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
+import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
import android.os.UserManager;
@@ -140,6 +141,20 @@
}
}
+ public void testRestrictions() {
+ List<UserInfo> users = mUserManager.getUsers();
+ if (users.size() > 1) {
+ Bundle restrictions = new Bundle();
+ restrictions.putBoolean(UserManager.ALLOW_INSTALL_APPS, false);
+ restrictions.putBoolean(UserManager.ALLOW_CONFIG_WIFI, true);
+ mUserManager.setUserRestrictions(restrictions, users.get(1).id);
+ Bundle stored = mUserManager.getUserRestrictions(users.get(1).id);
+ assertEquals(stored.getBoolean(UserManager.ALLOW_CONFIG_WIFI), true);
+ assertEquals(stored.getBoolean(UserManager.ALLOW_UNINSTALL_APPS), true);
+ assertEquals(stored.getBoolean(UserManager.ALLOW_INSTALL_APPS), false);
+ }
+ }
+
private void removeUser(int userId) {
synchronized (mUserLock) {
mUserManager.removeUser(userId);
@@ -151,4 +166,5 @@
}
}
}
+
}