Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit adea1918 authored by Benjamin Franz's avatar Benjamin Franz
Browse files

Make ContentObservers work across profiles

Currently registering for changes to a cross-user Uri does not work, as
the calling user id is used to identify the Uri. Change this to use the
userId the Uri is associated with. In order to protect Uris across
users, we only allow registration for a Uri when the caller has read
permission. We also only allow notify calls from across users when the
caller has write permission to the Uri.

Bug: 19312280
Change-Id: Ide216b09980ed5ebefe9b37c946dd8160167809f
parent 107cb81f
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -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()));
    }

    /**
+27 −12
Original line number Diff line number Diff line
@@ -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");
        }

        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();