Merge "Copy certain settings to the managed profile" into lmp-dev
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2338e39..9d1a7bc 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -738,7 +738,7 @@
/**
* Returns list of the profiles of userHandle including
* userHandle itself.
- * Note that it this returns both enabled and not enabled profiles. See
+ * Note that this returns both enabled and not enabled profiles. See
* {@link #getUserProfiles()} if you need only the enabled ones.
*
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8886559..c2a3012 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2667,6 +2667,16 @@
};
/**
+ * These entries are considered common between the personal and the managed profile,
+ * since the managed profile doesn't get to change them.
+ * @hide
+ */
+ public static final String[] CLONE_TO_MANAGED_PROFILE = {
+ DATE_FORMAT,
+ TIME_12_24
+ };
+
+ /**
* When to use Wi-Fi calling
*
* @see android.telephony.TelephonyManager.WifiCallingChoices
@@ -4797,6 +4807,26 @@
};
/**
+ * These entries are considered common between the personal and the managed profile,
+ * since the managed profile doesn't get to change them.
+ * @hide
+ */
+ public static final String[] CLONE_TO_MANAGED_PROFILE = {
+ ACCESSIBILITY_ENABLED,
+ ALLOW_MOCK_LOCATION,
+ ALLOWED_GEOLOCATION_ORIGINS,
+ DEFAULT_INPUT_METHOD,
+ ENABLED_ACCESSIBILITY_SERVICES,
+ ENABLED_INPUT_METHODS,
+ LOCATION_MODE,
+ LOCATION_PROVIDERS_ALLOWED,
+ LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ SELECTED_INPUT_METHOD_SUBTYPE,
+ SELECTED_SPELL_CHECKER,
+ SELECTED_SPELL_CHECKER_SUBTYPE
+ };
+
+ /**
* Helper method for determining if a location provider is enabled.
*
* @param cr the content resolver to use
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5dc7d26..87c015c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -20,6 +20,7 @@
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -94,6 +95,9 @@
// Each defined user has their own settings
protected final SparseArray<DatabaseHelper> mOpenHelpers = new SparseArray<DatabaseHelper>();
+ // Keep the list of managed profiles synced here
+ private List<UserInfo> mManagedProfiles = null;
+
// Over this size we don't reject loading or saving settings but
// we do consider them broken/malicious and don't keep them in
// memory at least:
@@ -119,6 +123,9 @@
private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";
+ static final HashSet<String> sSecureCloneToManagedKeys;
+ static final HashSet<String> sSystemCloneToManagedKeys;
+
static {
// Keys (name column) from the 'secure' table that are now in the owner user's 'global'
// table, shared across all users
@@ -142,6 +149,15 @@
UserManager.ENSURE_VERIFY_APPS);
sRestrictedKeys.put(Settings.Global.PREFERRED_NETWORK_MODE,
UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
+
+ sSecureCloneToManagedKeys = new HashSet<String>();
+ for (int i = 0; i < Settings.Secure.CLONE_TO_MANAGED_PROFILE.length; i++) {
+ sSecureCloneToManagedKeys.add(Settings.Secure.CLONE_TO_MANAGED_PROFILE[i]);
+ }
+ sSystemCloneToManagedKeys = new HashSet<String>();
+ for (int i = 0; i < Settings.System.CLONE_TO_MANAGED_PROFILE.length; i++) {
+ sSystemCloneToManagedKeys.add(Settings.System.CLONE_TO_MANAGED_PROFILE[i]);
+ }
}
private boolean settingMovedToGlobal(final String name) {
@@ -362,18 +378,22 @@
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ userFilter.addAction(Intent.ACTION_USER_ADDED);
getContext().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ UserHandle.USER_OWNER);
if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
- final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_OWNER);
- if (userHandle != UserHandle.USER_OWNER) {
- onUserRemoved(userHandle);
- }
+ onUserRemoved(userHandle);
+ } else if (intent.getAction().equals(Intent.ACTION_USER_ADDED)) {
+ onProfilesChanged();
}
}
}, userFilter);
+
+ onProfilesChanged();
+
return true;
}
@@ -391,6 +411,32 @@
sSystemCaches.delete(userHandle);
sSecureCaches.delete(userHandle);
sKnownMutationsInFlight.delete(userHandle);
+ onProfilesChanged();
+ }
+ }
+
+ /**
+ * Updates the list of managed profiles. It assumes that only the primary user
+ * can have managed profiles. Modify this code if that changes in the future.
+ */
+ void onProfilesChanged() {
+ synchronized (this) {
+ mManagedProfiles = mUserManager.getProfiles(UserHandle.USER_OWNER);
+ if (mManagedProfiles != null) {
+ // Remove the primary user from the list
+ for (int i = mManagedProfiles.size() - 1; i >= 0; i--) {
+ if (mManagedProfiles.get(i).id == UserHandle.USER_OWNER) {
+ mManagedProfiles.remove(i);
+ }
+ }
+ // If there are no managed profiles, reset the variable
+ if (mManagedProfiles.size() == 0) {
+ mManagedProfiles = null;
+ }
+ }
+ if (LOCAL_LOGV) {
+ Slog.d(TAG, "Managed Profiles = " + mManagedProfiles);
+ }
}
}
@@ -601,6 +647,24 @@
}
/**
+ * Checks if the calling user is a managed profile of the primary user.
+ * Currently only the primary user (USER_OWNER) can have managed profiles.
+ * @param callingUser the user trying to read/write settings
+ * @return true if it is a managed profile of the primary user
+ */
+ private boolean isManagedProfile(int callingUser) {
+ synchronized (this) {
+ if (mManagedProfiles == null) return false;
+ for (int i = mManagedProfiles.size() - 1; i >= 0; i--) {
+ if (mManagedProfiles.get(i).id == callingUser) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
* Fast path that avoids the use of chatty remoted Cursors.
*/
@Override
@@ -625,12 +689,18 @@
// Get methods
if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
if (LOCAL_LOGV) Slog.v(TAG, "call(system:" + request + ") for " + callingUser);
+ if (isManagedProfile(callingUser) && sSystemCloneToManagedKeys.contains(request)) {
+ callingUser = UserHandle.USER_OWNER;
+ }
dbHelper = getOrEstablishDatabase(callingUser);
cache = sSystemCaches.get(callingUser);
return lookupValue(dbHelper, TABLE_SYSTEM, cache, request);
}
if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
if (LOCAL_LOGV) Slog.v(TAG, "call(secure:" + request + ") for " + callingUser);
+ if (isManagedProfile(callingUser) && sSecureCloneToManagedKeys.contains(request)) {
+ callingUser = UserHandle.USER_OWNER;
+ }
dbHelper = getOrEstablishDatabase(callingUser);
cache = sSecureCaches.get(callingUser);
return lookupValue(dbHelper, TABLE_SECURE, cache, request);
@@ -667,13 +737,70 @@
values.put(Settings.NameValueTable.NAME, request);
values.put(Settings.NameValueTable.VALUE, newValue);
if (Settings.CALL_METHOD_PUT_SYSTEM.equals(method)) {
- if (LOCAL_LOGV) Slog.v(TAG, "call_put(system:" + request + "=" + newValue + ") for " + callingUser);
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "call_put(system:" + request + "=" + newValue + ") for "
+ + callingUser);
+ }
+ // Extra check for USER_OWNER to optimize for the 99%
+ if (callingUser != UserHandle.USER_OWNER && isManagedProfile(callingUser)) {
+ if (sSystemCloneToManagedKeys.contains(request)) {
+ // Don't write these settings
+ return null;
+ }
+ }
insertForUser(Settings.System.CONTENT_URI, values, callingUser);
+ // Clone the settings to the managed profiles so that notifications can be sent out
+ if (callingUser == UserHandle.USER_OWNER && mManagedProfiles != null
+ && sSystemCloneToManagedKeys.contains(request)) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ for (int i = mManagedProfiles.size() - 1; i >= 0; i--) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "putting to additional user "
+ + mManagedProfiles.get(i).id);
+ }
+ insertForUser(Settings.System.CONTENT_URI, values,
+ mManagedProfiles.get(i).id);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
} else if (Settings.CALL_METHOD_PUT_SECURE.equals(method)) {
- if (LOCAL_LOGV) Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for " + callingUser);
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for "
+ + callingUser);
+ }
+ // Extra check for USER_OWNER to optimize for the 99%
+ if (callingUser != UserHandle.USER_OWNER && isManagedProfile(callingUser)) {
+ if (sSecureCloneToManagedKeys.contains(request)) {
+ // Don't write these settings
+ return null;
+ }
+ }
insertForUser(Settings.Secure.CONTENT_URI, values, callingUser);
+ // Clone the settings to the managed profiles so that notifications can be sent out
+ if (callingUser == UserHandle.USER_OWNER && mManagedProfiles != null
+ && sSecureCloneToManagedKeys.contains(request)) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ for (int i = mManagedProfiles.size() - 1; i >= 0; i--) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "putting to additional user "
+ + mManagedProfiles.get(i).id);
+ }
+ insertForUser(Settings.Secure.CONTENT_URI, values,
+ mManagedProfiles.get(i).id);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
} else if (Settings.CALL_METHOD_PUT_GLOBAL.equals(method)) {
- if (LOCAL_LOGV) Slog.v(TAG, "call_put(global:" + request + "=" + newValue + ") for " + callingUser);
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "call_put(global:" + request + "=" + newValue + ") for "
+ + callingUser);
+ }
insertForUser(Settings.Global.CONTENT_URI, values, callingUser);
} else {
Slog.w(TAG, "call() with invalid method: " + method);