Skip to content

Commit b756445

Browse files
author
Christopher Tate
committed
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
1 parent 314488b commit b756445

File tree

3 files changed

+55
-57
lines changed

3 files changed

+55
-57
lines changed

core/java/android/content/ContentResolver.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,7 +1219,7 @@ public final ContentProviderClient acquireUnstableContentProviderClient(String n
12191219
public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
12201220
ContentObserver observer)
12211221
{
1222-
registerContentObserver(uri, notifyForDescendents, observer, UserHandle.myUserId());
1222+
registerContentObserver(uri, notifyForDescendents, observer, UserHandle.getCallingUserId());
12231223
}
12241224

12251225
/** @hide - designated user version */
@@ -1283,7 +1283,7 @@ public void notifyChange(Uri uri, ContentObserver observer) {
12831283
* @see #requestSync(android.accounts.Account, String, android.os.Bundle)
12841284
*/
12851285
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
1286-
notifyChange(uri, observer, syncToNetwork, UserHandle.myUserId());
1286+
notifyChange(uri, observer, syncToNetwork, UserHandle.getCallingUserId());
12871287
}
12881288

12891289
/**

core/java/android/content/ContentService.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,11 @@ public void registerContentObserver(Uri uri, boolean notifyForDescendants,
154154
throw new IllegalArgumentException("You must pass a valid uri and observer");
155155
}
156156

157-
// STOPSHIP: disable the multi-user permission checks until a solid fix for the
158-
// content provider / observer case is in place.
159-
/*
160157
final int callingUser = UserHandle.getCallingUserId();
161158
if (callingUser != userHandle) {
162159
mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
163160
"no permission to observe other users' provider view");
164161
}
165-
*/
166162

167163
if (userHandle < 0) {
168164
if (userHandle == UserHandle.USER_CURRENT) {

packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java

Lines changed: 53 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import java.util.concurrent.atomic.AtomicInteger;
2424

2525
import android.app.ActivityManager;
26-
import android.app.ActivityManagerNative;
2726
import android.app.backup.BackupManager;
2827
import android.content.BroadcastReceiver;
2928
import android.content.ContentProvider;
@@ -33,20 +32,17 @@
3332
import android.content.Intent;
3433
import android.content.IntentFilter;
3534
import android.content.pm.PackageManager;
36-
import android.content.pm.UserInfo;
3735
import android.content.res.AssetFileDescriptor;
3836
import android.database.Cursor;
3937
import android.database.sqlite.SQLiteDatabase;
4038
import android.database.sqlite.SQLiteException;
4139
import android.database.sqlite.SQLiteQueryBuilder;
42-
import android.database.sqlite.SQLiteStatement;
4340
import android.media.RingtoneManager;
4441
import android.net.Uri;
4542
import android.os.Binder;
4643
import android.os.Bundle;
4744
import android.os.FileObserver;
4845
import android.os.ParcelFileDescriptor;
49-
import android.os.RemoteException;
5046
import android.os.SystemProperties;
5147
import android.os.UserHandle;
5248
import android.os.UserManager;
@@ -656,53 +652,59 @@ public Bundle call(String method, String request, Bundle args) {
656652
}
657653
}
658654

659-
// Note: we assume that get/put operations for moved-to-global names have already
660-
// been directed to the new location on the caller side (otherwise we'd fix them
661-
// up here).
662-
663-
DatabaseHelper dbHelper;
664-
SettingsCache cache;
665-
666-
// Get methods
667-
if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
668-
if (LOCAL_LOGV) Slog.v(TAG, "call(system:" + request + ") for " + callingUser);
669-
dbHelper = getOrEstablishDatabase(callingUser);
670-
cache = sSystemCaches.get(callingUser);
671-
return lookupValue(dbHelper, TABLE_SYSTEM, cache, request);
672-
}
673-
if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
674-
if (LOCAL_LOGV) Slog.v(TAG, "call(secure:" + request + ") for " + callingUser);
675-
dbHelper = getOrEstablishDatabase(callingUser);
676-
cache = sSecureCaches.get(callingUser);
677-
return lookupValue(dbHelper, TABLE_SECURE, cache, request);
678-
}
679-
if (Settings.CALL_METHOD_GET_GLOBAL.equals(method)) {
680-
if (LOCAL_LOGV) Slog.v(TAG, "call(global:" + request + ") for " + callingUser);
681-
// fast path: owner db & cache are immutable after onCreate() so we need not
682-
// guard on the attempt to look them up
683-
return lookupValue(getOrEstablishDatabase(UserHandle.USER_OWNER), TABLE_GLOBAL,
684-
sGlobalCache, request);
685-
}
655+
// Okay, permission checks have cleared. Reset to our own identity so we can
656+
// manipulate all users' data with impunity.
657+
long oldId = Binder.clearCallingIdentity();
658+
try {
659+
// Note: we assume that get/put operations for moved-to-global names have already
660+
// been directed to the new location on the caller side (otherwise we'd fix them
661+
// up here).
662+
DatabaseHelper dbHelper;
663+
SettingsCache cache;
664+
665+
// Get methods
666+
if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
667+
if (LOCAL_LOGV) Slog.v(TAG, "call(system:" + request + ") for " + callingUser);
668+
dbHelper = getOrEstablishDatabase(callingUser);
669+
cache = sSystemCaches.get(callingUser);
670+
return lookupValue(dbHelper, TABLE_SYSTEM, cache, request);
671+
}
672+
if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
673+
if (LOCAL_LOGV) Slog.v(TAG, "call(secure:" + request + ") for " + callingUser);
674+
dbHelper = getOrEstablishDatabase(callingUser);
675+
cache = sSecureCaches.get(callingUser);
676+
return lookupValue(dbHelper, TABLE_SECURE, cache, request);
677+
}
678+
if (Settings.CALL_METHOD_GET_GLOBAL.equals(method)) {
679+
if (LOCAL_LOGV) Slog.v(TAG, "call(global:" + request + ") for " + callingUser);
680+
// fast path: owner db & cache are immutable after onCreate() so we need not
681+
// guard on the attempt to look them up
682+
return lookupValue(getOrEstablishDatabase(UserHandle.USER_OWNER), TABLE_GLOBAL,
683+
sGlobalCache, request);
684+
}
686685

687-
// Put methods - new value is in the args bundle under the key named by
688-
// the Settings.NameValueTable.VALUE static.
689-
final String newValue = (args == null)
690-
? null : args.getString(Settings.NameValueTable.VALUE);
691-
692-
final ContentValues values = new ContentValues();
693-
values.put(Settings.NameValueTable.NAME, request);
694-
values.put(Settings.NameValueTable.VALUE, newValue);
695-
if (Settings.CALL_METHOD_PUT_SYSTEM.equals(method)) {
696-
if (LOCAL_LOGV) Slog.v(TAG, "call_put(system:" + request + "=" + newValue + ") for " + callingUser);
697-
insertForUser(Settings.System.CONTENT_URI, values, callingUser);
698-
} else if (Settings.CALL_METHOD_PUT_SECURE.equals(method)) {
699-
if (LOCAL_LOGV) Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for " + callingUser);
700-
insertForUser(Settings.Secure.CONTENT_URI, values, callingUser);
701-
} else if (Settings.CALL_METHOD_PUT_GLOBAL.equals(method)) {
702-
if (LOCAL_LOGV) Slog.v(TAG, "call_put(global:" + request + "=" + newValue + ") for " + callingUser);
703-
insertForUser(Settings.Global.CONTENT_URI, values, callingUser);
704-
} else {
705-
Slog.w(TAG, "call() with invalid method: " + method);
686+
// Put methods - new value is in the args bundle under the key named by
687+
// the Settings.NameValueTable.VALUE static.
688+
final String newValue = (args == null)
689+
? null : args.getString(Settings.NameValueTable.VALUE);
690+
691+
final ContentValues values = new ContentValues();
692+
values.put(Settings.NameValueTable.NAME, request);
693+
values.put(Settings.NameValueTable.VALUE, newValue);
694+
if (Settings.CALL_METHOD_PUT_SYSTEM.equals(method)) {
695+
if (LOCAL_LOGV) Slog.v(TAG, "call_put(system:" + request + "=" + newValue + ") for " + callingUser);
696+
insertForUser(Settings.System.CONTENT_URI, values, callingUser);
697+
} else if (Settings.CALL_METHOD_PUT_SECURE.equals(method)) {
698+
if (LOCAL_LOGV) Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for " + callingUser);
699+
insertForUser(Settings.Secure.CONTENT_URI, values, callingUser);
700+
} else if (Settings.CALL_METHOD_PUT_GLOBAL.equals(method)) {
701+
if (LOCAL_LOGV) Slog.v(TAG, "call_put(global:" + request + "=" + newValue + ") for " + callingUser);
702+
insertForUser(Settings.Global.CONTENT_URI, values, callingUser);
703+
} else {
704+
Slog.w(TAG, "call() with invalid method: " + method);
705+
}
706+
} finally {
707+
Binder.restoreCallingIdentity(oldId);
706708
}
707709

708710
return null;
@@ -758,7 +760,7 @@ public Cursor query(Uri url, String[] select, String where, String[] whereArgs,
758760
return queryForUser(url, select, where, whereArgs, sort, UserHandle.getCallingUserId());
759761
}
760762

761-
public Cursor queryForUser(Uri url, String[] select, String where, String[] whereArgs,
763+
private Cursor queryForUser(Uri url, String[] select, String where, String[] whereArgs,
762764
String sort, int forUser) {
763765
if (LOCAL_LOGV) Slog.v(TAG, "query(" + url + ") for user " + forUser);
764766
SqlArguments args = new SqlArguments(url, where, whereArgs);

0 commit comments

Comments
 (0)