diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index d37dda049b6cf..6ede29b9ba156 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -1593,7 +1593,11 @@ public abstract class ContentResolver { @NonNull ContentObserver observer) { Preconditions.checkNotNull(uri, "uri"); Preconditions.checkNotNull(observer, "observer"); - registerContentObserver(uri, notifyForDescendents, observer, UserHandle.myUserId()); + registerContentObserver( + ContentProvider.getUriWithoutUserId(uri), + notifyForDescendents, + observer, + ContentProvider.getUserIdFromUri(uri, UserHandle.myUserId())); } /** @hide - designated user version */ @@ -1659,7 +1663,11 @@ public abstract class ContentResolver { public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer, boolean syncToNetwork) { Preconditions.checkNotNull(uri, "uri"); - notifyChange(uri, observer, syncToNetwork, UserHandle.myUserId()); + notifyChange( + ContentProvider.getUriWithoutUserId(uri), + observer, + syncToNetwork, + ContentProvider.getUserIdFromUri(uri, UserHandle.myUserId())); } /** diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 93ed2ee0c7a2a..2c57833765366 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -23,12 +23,14 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.IContentService; +import android.content.Intent; import android.content.ISyncStatusObserver; import android.content.PeriodicSync; import android.content.SyncAdapterType; import android.content.SyncInfo; import android.content.SyncRequest; import android.content.SyncStatusInfo; +import android.content.pm.PackageManager; import android.database.IContentObserver; import android.database.sqlite.SQLiteException; import android.net.Uri; @@ -161,8 +163,9 @@ public final class ContentService extends IContentService.Stub { * Register a content observer tied to a specific user's view of the provider. * @param userHandle the user whose view of the provider is to be observed. May be * the calling user without requiring any permission, otherwise the caller needs to - * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and - * USER_CURRENT are properly handled; all other pseudousers are forbidden. + * hold the INTERACT_ACROSS_USERS_FULL permission or hold a read uri grant to the uri. + * Pseudousers USER_ALL and USER_CURRENT are properly handled; all other pseudousers + * are forbidden. */ @Override public void registerContentObserver(Uri uri, boolean notifyForDescendants, @@ -171,8 +174,17 @@ public final class ContentService extends IContentService.Stub { throw new IllegalArgumentException("You must pass a valid uri and observer"); } - enforceCrossUserPermission(userHandle, - "no permission to observe other users' provider view"); + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); + final int callingUserHandle = UserHandle.getCallingUserId(); + // Registering an observer for any user other than the calling user requires uri grant or + // cross user permission + if (callingUserHandle != userHandle && + mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION) + != PackageManager.PERMISSION_GRANTED) { + enforceCrossUserPermission(userHandle, + "no permission to observe other users' provider view"); + } if (userHandle < 0) { if (userHandle == UserHandle.USER_CURRENT) { @@ -185,7 +197,7 @@ public final class ContentService extends IContentService.Stub { synchronized (mRootNode) { mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode, - Binder.getCallingUid(), Binder.getCallingPid(), userHandle); + uid, pid, userHandle); if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri + " with notifyForDescendants " + notifyForDescendants); } @@ -211,8 +223,9 @@ public final class ContentService extends IContentService.Stub { * Notify observers of a particular user's view of the provider. * @param userHandle the user whose view of the provider is to be notified. May be * the calling user without requiring any permission, otherwise the caller needs to - * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and - * USER_CURRENT are properly interpreted; no other pseudousers are allowed. + * hold the INTERACT_ACROSS_USERS_FULL permission or hold a write uri grant to the uri. + * Pseudousers USER_ALL and USER_CURRENT are properly interpreted; no other pseudousers are + * allowed. */ @Override public void notifyChange(Uri uri, IContentObserver observer, @@ -223,11 +236,14 @@ public final class ContentService extends IContentService.Stub { + " from observer " + observer + ", syncToNetwork " + syncToNetwork); } - // Notify for any user other than the caller's own requires permission. + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); final int callingUserHandle = UserHandle.getCallingUserId(); - if (userHandle != callingUserHandle) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS, - "no permission to notify other users"); + // Notify for any user other than the caller requires uri grant or cross user permission + if (callingUserHandle != userHandle && + mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + != PackageManager.PERMISSION_GRANTED) { + enforceCrossUserPermission(userHandle, "no permission to notify other users"); } // We passed the permission check; resolve pseudouser targets as appropriate @@ -240,7 +256,6 @@ public final class ContentService extends IContentService.Stub { } } - final int uid = Binder.getCallingUid(); // This makes it so that future permission checks will be in the context of this // process rather than the caller's process. We will restore this before returning. long identityToken = clearCallingIdentity();