summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Tate <ctate@google.com>2012-09-19 16:21:18 -0700
committerChristopher Tate <ctate@google.com>2012-09-20 13:58:16 -0700
commitb7564454297ba1706670ccab0562cac6676d0a77 (patch)
treed52fa66f9729feb3250a20eba04fca2a70a40561
parent314488b791d36756504ee593f818f04b4cd24893 (diff)
downloadframeworks_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
-rw-r--r--core/java/android/content/ContentResolver.java4
-rw-r--r--core/java/android/content/ContentService.java4
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java104
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);