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

Commit 35d81ecd authored by Jeff DeCew's avatar Jeff DeCew Committed by Automerger Merge Worker
Browse files

Revoke Uri access after a NotificationListener is removed. am: b2823613

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/12434415

Change-Id: I459c36327adf8628159b60e63ef35b3a1ade936d
parents 3f151d9d b2823613
Loading
Loading
Loading
Loading
+55 −26
Original line number Diff line number Diff line
@@ -7737,6 +7737,13 @@ public class NotificationManagerService extends SystemService {
    @VisibleForTesting
    void updateUriPermissions(@Nullable NotificationRecord newRecord,
            @Nullable NotificationRecord oldRecord, String targetPkg, int targetUserId) {
        updateUriPermissions(newRecord, oldRecord, targetPkg, targetUserId, false);
    }

    @VisibleForTesting
    void updateUriPermissions(@Nullable NotificationRecord newRecord,
            @Nullable NotificationRecord oldRecord, String targetPkg, int targetUserId,
            boolean onlyRevokeCurrentTarget) {
        final String key = (newRecord != null) ? newRecord.getKey() : oldRecord.getKey();
        if (DBG) Slog.d(TAG, key + ": updating permissions");

@@ -7764,7 +7771,9 @@ public class NotificationManagerService extends SystemService {
        }

        // If we have no Uris to grant, but an existing owner, go destroy it
        if (newUris == null && permissionOwner != null) {
        // When revoking permissions of a single listener, destroying the owner will revoke
        // permissions of other listeners who need to keep access.
        if (newUris == null && permissionOwner != null && !onlyRevokeCurrentTarget) {
            destroyPermissionOwner(permissionOwner, UserHandle.getUserId(oldRecord.getUid()), key);
            permissionOwner = null;
        }
@@ -7787,9 +7796,20 @@ public class NotificationManagerService extends SystemService {
                final Uri uri = oldUris.valueAt(i);
                if (newUris == null || !newUris.contains(uri)) {
                    if (DBG) Slog.d(TAG, key + ": revoking " + uri);
                    int userId = ContentProvider.getUserIdFromUri(
                            uri, UserHandle.getUserId(oldRecord.getUid()));
                    revokeUriPermission(permissionOwner, uri, userId);
                    if (onlyRevokeCurrentTarget) {
                        // We're revoking permission from one listener only; other listeners may
                        // still need access because the notification may still exist
                        revokeUriPermission(permissionOwner, uri,
                                UserHandle.getUserId(oldRecord.getUid()), targetPkg, targetUserId);
                    } else {
                        // This is broad to unilaterally revoke permissions to this Uri as granted
                        // by this notification.  But this code-path can only be used when the
                        // reason for revoking is that the notification posted again without this
                        // Uri, not when removing an individual listener.
                        revokeUriPermission(permissionOwner, uri,
                                UserHandle.getUserId(oldRecord.getUid()),
                                null, UserHandle.USER_ALL);
                    }
                }
            }
        }
@@ -7818,8 +7838,10 @@ public class NotificationManagerService extends SystemService {
        }
    }

    private void revokeUriPermission(IBinder owner, Uri uri, int userId) {
    private void revokeUriPermission(IBinder owner, Uri uri, int sourceUserId, String targetPkg,
            int targetUserId) {
        if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
        int userId = ContentProvider.getUserIdFromUri(uri, sourceUserId);

        final long ident = Binder.clearCallingIdentity();
        try {
@@ -7827,7 +7849,7 @@ public class NotificationManagerService extends SystemService {
                    owner,
                    ContentProvider.getUriWithoutUserId(uri),
                    Intent.FLAG_GRANT_READ_URI_PERMISSION,
                    userId);
                    userId, targetPkg, targetUserId);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
@@ -9195,7 +9217,7 @@ public class NotificationManagerService extends SystemService {
            final NotificationRankingUpdate update;
            synchronized (mNotificationLock) {
                update = makeRankingUpdateLocked(info);
                grantUriPermissionsForActiveNotificationsLocked(info);
                updateUriPermissionsForActiveNotificationsLocked(info, true);
            }
            try {
                listener.onListenerConnected(update);
@@ -9207,6 +9229,7 @@ public class NotificationManagerService extends SystemService {
        @Override
        @GuardedBy("mNotificationLock")
        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
            updateUriPermissionsForActiveNotificationsLocked(removed, false);
            if (removeDisabledHints(removed)) {
                updateListenerHintsLocked();
                updateEffectsSuppressorLocked();
@@ -9273,8 +9296,7 @@ public class NotificationManagerService extends SystemService {

                for (final ManagedServiceInfo info : getServices()) {
                    boolean sbnVisible = isVisibleToListener(sbn, info);
                    boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info)
                            : false;
                    boolean oldSbnVisible = (oldSbn != null) && isVisibleToListener(oldSbn, info);
                    // This notification hasn't been and still isn't visible -> ignore.
                    if (!oldSbnVisible && !sbnVisible) {
                        continue;
@@ -9317,28 +9339,42 @@ public class NotificationManagerService extends SystemService {
        }

        /**
         * Synchronously grant permissions to Uris for all active and visible notifications to the
         * NotificationListenerService provided.
         * Synchronously grant or revoke permissions to Uris for all active and visible
         * notifications to just the NotificationListenerService provided.
         */
        @GuardedBy("mNotificationLock")
        private void grantUriPermissionsForActiveNotificationsLocked(ManagedServiceInfo info) {
        private void updateUriPermissionsForActiveNotificationsLocked(
                ManagedServiceInfo info, boolean grant) {
            try {
                for (final NotificationRecord r : mNotificationList) {
                    // This notification isn't visible -> ignore.
                    if (!isVisibleToListener(r.getSbn(), info)) {
                    // When granting permissions, ignore notifications which are invisible.
                    // When revoking permissions, all notifications are invisible, so process all.
                    if (grant && !isVisibleToListener(r.getSbn(), info)) {
                        continue;
                    }
                    // If the notification is hidden, permissions are not required by the listener.
                    if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) {
                        continue;
                    }
                    // Grant access before listener is initialized
                    // Grant or revoke access synchronously
                    final int targetUserId = (info.userid == UserHandle.USER_ALL)
                            ? UserHandle.USER_SYSTEM : info.userid;
                    updateUriPermissions(r, null, info.component.getPackageName(), targetUserId);
                    if (grant) {
                        // Grant permissions by passing arguments as if the notification is new.
                        updateUriPermissions(/* newRecord */ r, /* oldRecord */ null,
                                info.component.getPackageName(), targetUserId);
                    } else {
                        // Revoke permissions by passing arguments as if the notification was
                        // removed, but set `onlyRevokeCurrentTarget` to avoid revoking permissions
                        // granted to *other* targets by this notification's URIs.
                        updateUriPermissions(/* newRecord */ null, /* oldRecord */ r,
                                info.component.getPackageName(), targetUserId,
                                /* onlyRevokeCurrentTarget */ true);
                    }
                }
            } catch (Exception e) {
                Slog.e(TAG, "Could not grant Uri permissions to " + info.component, e);
                Slog.e(TAG, "Could not " + (grant ? "grant" : "revoke") + " Uri permissions to "
                        + info.component, e);
            }
        }

@@ -9377,18 +9413,11 @@ public class NotificationManagerService extends SystemService {
                final NotificationStats stats = mAssistants.isServiceTokenValidLocked(info.service)
                        ? notificationStats : null;
                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyRemoved(info, sbnLight, update, stats, reason);
                    }
                });
                mHandler.post(() -> notifyRemoved(info, sbnLight, update, stats, reason));
            }

            // Revoke access after all listeners have been updated
            mHandler.post(() -> {
                updateUriPermissions(null, r, null, UserHandle.USER_SYSTEM);
            });
            mHandler.post(() -> updateUriPermissions(null, r, null, UserHandle.USER_SYSTEM));
        }

        /**
+22 −1
Original line number Diff line number Diff line
@@ -75,10 +75,31 @@ public interface UriGrantsManagerInternal {
    void removeUriPermissionsForPackage(
            String packageName, int userHandle, boolean persistable, boolean targetOnly);
    /**
     * @param uri This uri must NOT contain an embedded userId.
     * Remove any {@link UriPermission} associated with the owner whose values match the given
     * filtering parameters.
     *
     * @param token An opaque owner token as returned by {@link #newUriPermissionOwner(String)}.
     * @param uri This uri must NOT contain an embedded userId. {@code null} to apply to all Uris.
     * @param mode The modes (as a bitmask) to revoke.
     * @param userId The userId in which the uri is to be resolved.
     */
    void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId);

    /**
     * Remove any {@link UriPermission} associated with the owner whose values match the given
     * filtering parameters.
     *
     * @param token An opaque owner token as returned by {@link #newUriPermissionOwner(String)}.
     * @param uri This uri must NOT contain an embedded userId. {@code null} to apply to all Uris.
     * @param mode The modes (as a bitmask) to revoke.
     * @param userId The userId in which the uri is to be resolved.
     * @param targetPkg Calling package name to match, or {@code null} to apply to all packages.
     * @param targetUserId Calling user to match, or {@link UserHandle#USER_ALL} to apply to all
     *                     users.
     */
    void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId,
            String targetPkg, int targetUserId);

    boolean checkAuthorityGrants(
            int callingUid, ProviderInfo cpi, int userId, boolean checkUser);
    void dump(PrintWriter pw, boolean dumpAll, String dumpPackage);
+10 −9
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@ import android.app.AppGlobals;
import android.app.GrantedUriPermission;
import android.app.IUriGrantsManager;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -88,11 +87,11 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;

import libcore.io.IoUtils;

import com.google.android.collect.Lists;
import com.google.android.collect.Maps;

import libcore.io.IoUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -1431,16 +1430,18 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {

        @Override
        public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId) {
            revokeUriPermissionFromOwner(token, uri, mode, userId, null, UserHandle.USER_ALL);
        }

        @Override
        public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId,
                String targetPkg, int targetUserId) {
            final UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
            if (owner == null) {
                throw new IllegalArgumentException("Unknown owner: " + token);
            }

            if (uri == null) {
                owner.removeUriPermissions(mode);
            } else {
                owner.removeUriPermission(new GrantUri(userId, uri, mode), mode);
            }
            GrantUri grantUri = uri == null ? null : new GrantUri(userId, uri, mode);
            owner.removeUriPermission(grantUri, mode, targetPkg, targetUserId);
        }

        @Override
+28 −10
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;

import android.os.Binder;
import android.os.IBinder;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.proto.ProtoOutputStream;

@@ -74,31 +75,48 @@ public class UriPermissionOwner {
    }

    void removeUriPermission(GrantUri grantUri, int mode) {
        removeUriPermission(grantUri, mode, null, UserHandle.USER_ALL);
    }

    void removeUriPermission(GrantUri grantUri, int mode, String targetPgk, int targetUserId) {
        if ((mode & FLAG_GRANT_READ_URI_PERMISSION) != 0 && mReadPerms != null) {
            Iterator<UriPermission> it = mReadPerms.iterator();
            while (it.hasNext()) {
                UriPermission perm = it.next();
                if (grantUri == null || grantUri.equals(perm.uri)) {
                if (grantUri != null && !grantUri.equals(perm.uri)) {
                    continue;
                }
                if (targetPgk != null && !targetPgk.equals(perm.targetPkg)) {
                    continue;
                }
                if (targetUserId != UserHandle.USER_ALL && targetUserId != perm.targetUserId) {
                    continue;
                }
                perm.removeReadOwner(this);
                mService.removeUriPermissionIfNeeded(perm);
                it.remove();
            }
            }
            if (mReadPerms.isEmpty()) {
                mReadPerms = null;
            }
        }
        if ((mode & FLAG_GRANT_WRITE_URI_PERMISSION) != 0
                && mWritePerms != null) {
        if ((mode & FLAG_GRANT_WRITE_URI_PERMISSION) != 0 && mWritePerms != null) {
            Iterator<UriPermission> it = mWritePerms.iterator();
            while (it.hasNext()) {
                UriPermission perm = it.next();
                if (grantUri == null || grantUri.equals(perm.uri)) {
                if (grantUri != null && !grantUri.equals(perm.uri)) {
                    continue;
                }
                if (targetPgk != null && !targetPgk.equals(perm.targetPkg)) {
                    continue;
                }
                if (targetUserId != UserHandle.USER_ALL && targetUserId != perm.targetUserId) {
                    continue;
                }
                perm.removeWriteOwner(this);
                mService.removeUriPermissionIfNeeded(perm);
                it.remove();
            }
            }
            if (mWritePerms.isEmpty()) {
                mWritePerms = null;
            }
+1 −1
Original line number Diff line number Diff line
@@ -3865,7 +3865,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        mService.updateUriPermissions(recordB, recordA, mContext.getPackageName(),
                USER_SYSTEM);
        verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(any(),
                eq(message1.getDataUri()), anyInt(), anyInt());
                eq(message1.getDataUri()), anyInt(), anyInt(), eq(null), eq(-1));

        // Update back means we grant access to first again
        reset(mUgm);