diff options
author | Christopher Tate <ctate@google.com> | 2012-09-19 16:21:18 -0700 |
---|---|---|
committer | Christopher Tate <ctate@google.com> | 2012-09-20 13:58:16 -0700 |
commit | b7564454297ba1706670ccab0562cac6676d0a77 (patch) | |
tree | d52fa66f9729feb3250a20eba04fca2a70a40561 | |
parent | 314488b791d36756504ee593f818f04b4cd24893 (diff) | |
download | frameworks_base-b7564454297ba1706670ccab0562cac6676d0a77.zip frameworks_base-b7564454297ba1706670ccab0562cac6676d0a77.tar.gz frameworks_base-b7564454297ba1706670ccab0562cac6676d0a77.tar.bz2 |
Multiuser awareness in content observer infrastructure
Content observers are registered under the calling user's identity,
not under the provider host's identity (unless the caller is using
the new permissioned entry points that allow observers to be
registered for a different user's view of the data). The most important
implication of this is that when the settings provider is directly
queried, the Cursor returned to the app is wired for change notifications
based on that calling app's user.
Note that it is not possible to use query() / insert() to read/write
data for different users. All such manipulations should use the
standard get* / put* methods in Settings.*.
Bug 7122169
Change-Id: If5d9ec44927e5e56e4e7635438f4ef48a5430986
3 files changed, 55 insertions, 57 deletions
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index cd1e882..4ab8272 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -1219,7 +1219,7 @@ public abstract class ContentResolver { public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer) { - registerContentObserver(uri, notifyForDescendents, observer, UserHandle.myUserId()); + registerContentObserver(uri, notifyForDescendents, observer, UserHandle.getCallingUserId()); } /** @hide - designated user version */ @@ -1283,7 +1283,7 @@ public abstract class ContentResolver { * @see #requestSync(android.accounts.Account, String, android.os.Bundle) */ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { - notifyChange(uri, observer, syncToNetwork, UserHandle.myUserId()); + notifyChange(uri, observer, syncToNetwork, UserHandle.getCallingUserId()); } /** diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java index 5986dcd..0f6488a 100644 --- a/core/java/android/content/ContentService.java +++ b/core/java/android/content/ContentService.java @@ -154,15 +154,11 @@ public final class ContentService extends IContentService.Stub { throw new IllegalArgumentException("You must pass a valid uri and observer"); } - // STOPSHIP: disable the multi-user permission checks until a solid fix for the - // content provider / observer case is in place. - /* final int callingUser = UserHandle.getCallingUserId(); if (callingUser != userHandle) { mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, "no permission to observe other users' provider view"); } - */ if (userHandle < 0) { if (userHandle == UserHandle.USER_CURRENT) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 9839c16..f0b8812 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -23,7 +23,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import android.app.ActivityManager; -import android.app.ActivityManagerNative; import android.app.backup.BackupManager; import android.content.BroadcastReceiver; import android.content.ContentProvider; @@ -33,20 +32,17 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.content.pm.UserInfo; import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteQueryBuilder; -import android.database.sqlite.SQLiteStatement; import android.media.RingtoneManager; import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.FileObserver; import android.os.ParcelFileDescriptor; -import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -656,53 +652,59 @@ public class SettingsProvider extends ContentProvider { } } - // Note: we assume that get/put operations for moved-to-global names have already - // been directed to the new location on the caller side (otherwise we'd fix them - // up here). - - DatabaseHelper dbHelper; - SettingsCache cache; - - // Get methods - if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) { - if (LOCAL_LOGV) Slog.v(TAG, "call(system:" + request + ") for " + callingUser); - 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); - dbHelper = getOrEstablishDatabase(callingUser); - cache = sSecureCaches.get(callingUser); - return lookupValue(dbHelper, TABLE_SECURE, cache, request); - } - if (Settings.CALL_METHOD_GET_GLOBAL.equals(method)) { - if (LOCAL_LOGV) Slog.v(TAG, "call(global:" + request + ") for " + callingUser); - // fast path: owner db & cache are immutable after onCreate() so we need not - // guard on the attempt to look them up - return lookupValue(getOrEstablishDatabase(UserHandle.USER_OWNER), TABLE_GLOBAL, - sGlobalCache, request); - } + // Okay, permission checks have cleared. Reset to our own identity so we can + // manipulate all users' data with impunity. + long oldId = Binder.clearCallingIdentity(); + try { + // Note: we assume that get/put operations for moved-to-global names have already + // been directed to the new location on the caller side (otherwise we'd fix them + // up here). + DatabaseHelper dbHelper; + SettingsCache cache; + + // Get methods + if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) { + if (LOCAL_LOGV) Slog.v(TAG, "call(system:" + request + ") for " + callingUser); + 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); + dbHelper = getOrEstablishDatabase(callingUser); + cache = sSecureCaches.get(callingUser); + return lookupValue(dbHelper, TABLE_SECURE, cache, request); + } + if (Settings.CALL_METHOD_GET_GLOBAL.equals(method)) { + if (LOCAL_LOGV) Slog.v(TAG, "call(global:" + request + ") for " + callingUser); + // fast path: owner db & cache are immutable after onCreate() so we need not + // guard on the attempt to look them up + return lookupValue(getOrEstablishDatabase(UserHandle.USER_OWNER), TABLE_GLOBAL, + sGlobalCache, request); + } - // Put methods - new value is in the args bundle under the key named by - // the Settings.NameValueTable.VALUE static. - final String newValue = (args == null) - ? null : args.getString(Settings.NameValueTable.VALUE); - - final ContentValues values = new ContentValues(); - 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); - insertForUser(Settings.System.CONTENT_URI, values, callingUser); - } else if (Settings.CALL_METHOD_PUT_SECURE.equals(method)) { - if (LOCAL_LOGV) Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for " + callingUser); - insertForUser(Settings.Secure.CONTENT_URI, values, callingUser); - } else if (Settings.CALL_METHOD_PUT_GLOBAL.equals(method)) { - 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); + // Put methods - new value is in the args bundle under the key named by + // the Settings.NameValueTable.VALUE static. + final String newValue = (args == null) + ? null : args.getString(Settings.NameValueTable.VALUE); + + final ContentValues values = new ContentValues(); + 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); + insertForUser(Settings.System.CONTENT_URI, values, callingUser); + } else if (Settings.CALL_METHOD_PUT_SECURE.equals(method)) { + if (LOCAL_LOGV) Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for " + callingUser); + insertForUser(Settings.Secure.CONTENT_URI, values, callingUser); + } else if (Settings.CALL_METHOD_PUT_GLOBAL.equals(method)) { + 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); + } + } finally { + Binder.restoreCallingIdentity(oldId); } return null; @@ -758,7 +760,7 @@ public class SettingsProvider extends ContentProvider { return queryForUser(url, select, where, whereArgs, sort, UserHandle.getCallingUserId()); } - public Cursor queryForUser(Uri url, String[] select, String where, String[] whereArgs, + private Cursor queryForUser(Uri url, String[] select, String where, String[] whereArgs, String sort, int forUser) { if (LOCAL_LOGV) Slog.v(TAG, "query(" + url + ") for user " + forUser); SqlArguments args = new SqlArguments(url, where, whereArgs); |