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

Commit 348f19e6 authored by Julia Reynolds's avatar Julia Reynolds Committed by Android (Google) Code Review
Browse files

Merge "Grant uri access to notification listeners."

parents 46de390f e0d711f4
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -1055,11 +1055,10 @@ public class Notification implements Parcelable
    /**
     * {@link #extras} key: A
     * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
     * in the background when the notification is selected. The URI must point to an image stream
     * suitable for passing into
     * in the background when the notification is selected. Used on television platforms.
     * The URI must point to an image stream suitable for passing into
     * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
     * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
     * URI used for this purpose must require no permissions to read the image data.
     * BitmapFactory.decodeStream}; all other content types will be ignored.
     */
    public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";

+12 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.annotation.TestApi;
import android.app.Notification.Builder;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.drawable.Icon;
import android.net.Uri;
@@ -350,6 +351,14 @@ public class NotificationManager {
     * the same tag and id has already been posted by your application and has not yet been
     * canceled, it will be replaced by the updated information.
     *
     * All {@link android.service.notification.NotificationListenerService listener services} will
     * be granted {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} access to any {@link Uri uris}
     * provided on this notification or the
     * {@link NotificationChannel} this notification is posted to using
     * {@link Context#grantUriPermission(String, Uri, int)}. Permission will be revoked when the
     * notification is canceled, or you can revoke permissions with
     * {@link Context#revokeUriPermission(Uri, int)}.
     *
     * @param tag A string identifier for this notification.  May be {@code null}.
     * @param id An identifier for this notification.  The pair (tag, id) must be unique
     *        within your application.
@@ -370,11 +379,13 @@ public class NotificationManager {
        String pkg = mContext.getPackageName();
        // Fix the notification as best we can.
        Notification.addFieldsFromContext(mContext, notification);

        if (notification.sound != null) {
            notification.sound = notification.sound.getCanonicalUri();
            if (StrictMode.vmFileUriExposureEnabled()) {
                notification.sound.checkFileUriExposed("Notification.sound");
            }

        }
        fixLegacySmallIcon(notification, pkg);
        if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
@@ -385,6 +396,7 @@ public class NotificationManager {
        }
        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
        notification.reduceImageSizes(mContext);

        ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
        boolean isLowRam = am.isLowRamDevice();
        final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam);
+4 −4
Original line number Diff line number Diff line
@@ -376,9 +376,7 @@ abstract public class ManagedServices {
    protected void upgradeXml(final int xmlVersion, final int userId) {}

    private void loadAllowedComponentsFromSettings() {

        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
        for (UserInfo user : userManager.getUsers()) {
        for (UserInfo user : mUm.getUsers()) {
            final ContentResolver cr = mContext.getContentResolver();
            addApprovedList(Settings.Secure.getStringForUser(
                    cr,
@@ -482,7 +480,9 @@ abstract public class ManagedServices {
        for (int i = 0; i < allowedByType.size(); i++) {
            final ArraySet<String> allowed = allowedByType.valueAt(i);
            allowedPackages.addAll(
                    allowed.stream().map(this::getPackageName).collect(Collectors.toList()));
                    allowed.stream().map(this::getPackageName).
                            filter(value -> !TextUtils.isEmpty(value))
                            .collect(Collectors.toList()));
        }
        return allowedPackages;
    }
+81 −12
Original line number Diff line number Diff line
@@ -25,7 +25,9 @@ import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_NULL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.NotificationListenerService
        .HINT_HOST_DISABLE_CALL_EFFECTS;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
@@ -88,6 +90,7 @@ import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -115,6 +118,7 @@ import android.os.IDeviceIdleController;
import android.os.IInterface;
import android.os.Looper;
import android.os.Message;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -206,6 +210,7 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
@@ -288,6 +293,7 @@ public class NotificationManagerService extends SystemService {
    private ICompanionDeviceManager mCompanionManager;
    private AccessibilityManager mAccessibilityManager;
    private IDeviceIdleController mDeviceIdleController;
    private IBinder mPermissionOwner;

    final IBinder mForegroundToken = new Binder();
    private WorkerHandler mHandler;
@@ -524,7 +530,7 @@ public class NotificationManagerService extends SystemService {
            } catch (FileNotFoundException e) {
                // No data yet
                // Load default managed services approvals
                readDefaultApprovedServices(UserHandle.USER_SYSTEM);
                readDefaultApprovedServices(USER_SYSTEM);
            } catch (IOException e) {
                Log.wtf(TAG, "Unable to read notification policy", e);
            } catch (NumberFormatException e) {
@@ -974,7 +980,7 @@ public class NotificationManagerService extends SystemService {
                            final int enabled = mPackageManager.getApplicationEnabledSetting(
                                    pkgName,
                                    changeUserId != UserHandle.USER_ALL ? changeUserId :
                                            UserHandle.USER_SYSTEM);
                                            USER_SYSTEM);
                            if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                                    || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
                                cancelNotifications = false;
@@ -1061,6 +1067,7 @@ public class NotificationManagerService extends SystemService {
                }
            } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
                final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
                mUserProfiles.updateCache(context);
                mZenModeHelper.onUserRemoved(user);
                mRankingHelper.onUserRemoved(user);
                mListeners.onUserRemoved(user);
@@ -1268,7 +1275,7 @@ public class NotificationManagerService extends SystemService {
            NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
            ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
            NotificationUsageStats usageStats, AtomicFile policyFile,
            ActivityManager activityManager, GroupHelper groupHelper) {
            ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am) {
        Resources resources = getContext().getResources();
        mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
                Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -1276,7 +1283,7 @@ public class NotificationManagerService extends SystemService {

        mAccessibilityManager =
                (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
        mAm = ActivityManager.getService();
        mAm = am;
        mPackageManager = packageManager;
        mPackageManagerClient = packageManagerClient;
        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
@@ -1287,6 +1294,11 @@ public class NotificationManagerService extends SystemService {
        mActivityManager = activityManager;
        mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
                ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
        try {
            mPermissionOwner = mAm.newUriPermissionOwner("notification");
        } catch (RemoteException e) {
            Slog.w(TAG, "AM dead", e);
        }

        mHandler = new WorkerHandler(looper);
        mRankingThread.start();
@@ -1415,7 +1427,7 @@ public class NotificationManagerService extends SystemService {
                null, snoozeHelper, new NotificationUsageStats(getContext()),
                new AtomicFile(new File(systemDir, "notification_policy.xml"), "notification-policy"),
                (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
                getGroupHelper());
                getGroupHelper(), ActivityManager.getService());

        // register for various Intents
        IntentFilter filter = new IntentFilter();
@@ -1749,7 +1761,7 @@ public class NotificationManagerService extends SystemService {
    protected void reportSeen(NotificationRecord r) {
        final int userId = r.sbn.getUserId();
        mAppUsageStats.reportEvent(r.sbn.getPackageName(),
                userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
                userId == UserHandle.USER_ALL ? USER_SYSTEM
                        : userId,
                UsageEvents.Event.NOTIFICATION_SEEN);
    }
@@ -2894,7 +2906,7 @@ public class NotificationManagerService extends SystemService {
            checkCallerIsSystem();
            if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
            //TODO: http://b/22388012
            if (user != UserHandle.USER_SYSTEM) {
            if (user != USER_SYSTEM) {
                Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
                return null;
            }
@@ -2920,7 +2932,7 @@ public class NotificationManagerService extends SystemService {
                return;
            }
            //TODO: http://b/22388012
            if (user != UserHandle.USER_SYSTEM) {
            if (user != USER_SYSTEM) {
                Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
                return;
            }
@@ -3678,7 +3690,7 @@ public class NotificationManagerService extends SystemService {
            sbn.getNotification().flags =
                    (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
            mRankingHelper.sort(mNotificationList);
            mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
            mListeners.notifyPostedLocked(r, sbn /* oldSbn */);
        }
    };

@@ -3707,7 +3719,7 @@ public class NotificationManagerService extends SystemService {
        try {
            final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
                    pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                    (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
                    (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
            Notification.addFieldsFromContext(ai, notification);

            int canColorize = mPackageManagerClient.checkPermission(
@@ -4126,6 +4138,8 @@ public class NotificationManagerService extends SystemService {
                        // Make sure we don't lose the foreground service state.
                        notification.flags |=
                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
                        // revoke uri permissions for changed uris
                        revokeUriPermissions(r, old);
                        r.isUpdate = true;
                    }

@@ -4147,7 +4161,7 @@ public class NotificationManagerService extends SystemService {

                    if (notification.getSmallIcon() != null) {
                        StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
                        mListeners.notifyPostedLocked(n, oldSbn);
                        mListeners.notifyPostedLocked(r, oldSbn);
                        if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
                            mHandler.post(new Runnable() {
                                @Override
@@ -4912,6 +4926,9 @@ public class NotificationManagerService extends SystemService {
            r.recordDismissalSurface(NotificationStats.DISMISSAL_OTHER);
        }

        // Revoke permissions
        revokeUriPermissions(null, r);

        // tell the app
        if (sendDelete) {
            if (r.getNotification().deleteIntent != null) {
@@ -5009,6 +5026,30 @@ public class NotificationManagerService extends SystemService {
                r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now), listenerName);
    }

    void revokeUriPermissions(NotificationRecord newRecord, NotificationRecord oldRecord) {
        Set<Uri> oldUris = oldRecord.getNotificationUris();
        Set<Uri> newUris = newRecord == null ? new HashSet<>() : newRecord.getNotificationUris();
        oldUris.removeAll(newUris);

        long ident = Binder.clearCallingIdentity();
        try {
            for (Uri uri : oldUris) {
                if (uri != null) {
                    int notiUserId = oldRecord.getUserId();
                    int sourceUserId = notiUserId == USER_ALL ? USER_SYSTEM
                            : ContentProvider.getUserIdFromUri(uri, notiUserId);
                    uri = ContentProvider.getUriWithoutUserId(uri);
                    mAm.revokeUriPermissionFromOwner(mPermissionOwner,
                            uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId);
                }
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Count not revoke uri permissions", e);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    /**
     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
     * and none of the {@code mustNotHaveFlags}.
@@ -5851,10 +5892,13 @@ public class NotificationManagerService extends SystemService {
         * but isn't anymore.
         */
        @GuardedBy("mNotificationLock")
        public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
        public void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn) {
            // Lazily initialized snapshots of the notification.
            StatusBarNotification sbn = r.sbn;
            TrimCache trimCache = new TrimCache(sbn);

            Set<Uri> uris = r.getNotificationUris();

            for (final ManagedServiceInfo info : getServices()) {
                boolean sbnVisible = isVisibleToListener(sbn, info);
                boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
@@ -5877,6 +5921,9 @@ public class NotificationManagerService extends SystemService {
                    continue;
                }

                grantUriPermissions(uris, sbn.getUserId(), info.component.getPackageName(),
                        info.userid);

                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
                mHandler.post(new Runnable() {
                    @Override
@@ -5887,6 +5934,28 @@ public class NotificationManagerService extends SystemService {
            }
        }

        private void grantUriPermissions(Set<Uri> uris, int notiUserId, String listenerPkg,
                int listenerUserId) {
            long ident = Binder.clearCallingIdentity();
            try {
                for (Uri uri : uris) {
                    if (uri != null) {
                        int sourceUserId = notiUserId == USER_ALL ? USER_SYSTEM
                                : ContentProvider.getUserIdFromUri(uri, notiUserId);
                        uri = ContentProvider.getUriWithoutUserId(uri);
                        mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(),
                                listenerPkg,
                                uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId,
                                listenerUserId == USER_ALL ? USER_SYSTEM : listenerUserId);
                    }
                }
            } catch (RemoteException e) {
                Log.e(TAG, "Count not grant uri permission to " + listenerPkg, e);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        /**
         * asynchronously notify all listeners about a removed notification
         */
+39 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.Adjustment;
@@ -47,6 +48,7 @@ import android.service.notification.NotificationStats;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
@@ -64,6 +66,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * Holds data about notifications that should not be shared with the
@@ -929,6 +932,42 @@ public final class NotificationRecord {
        mStats.setViewedSettings();
    }

    public Set<Uri> getNotificationUris() {
        Notification notification = getNotification();
        Set<Uri> uris = new ArraySet<>();

        if (notification.sound != null) {
            uris.add(notification.sound);
        }
        if (notification.getChannelId() != null) {
            NotificationChannel channel = getChannel();
            if (channel != null && channel.getSound() != null) {
                uris.add(channel.getSound());
            }
        }
        if (notification.extras.containsKey(Notification.EXTRA_AUDIO_CONTENTS_URI)) {
            uris.add(notification.extras.getParcelable(Notification.EXTRA_AUDIO_CONTENTS_URI));
        }
        if (notification.extras.containsKey(Notification.EXTRA_BACKGROUND_IMAGE_URI)) {
            uris.add(notification.extras.getParcelable(Notification.EXTRA_BACKGROUND_IMAGE_URI));
        }
        if (Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
            Parcelable[] newMessages =
                    notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
            List<Notification.MessagingStyle.Message> messages
                    = Notification.MessagingStyle.Message.getMessagesFromBundleArray(newMessages);
            Parcelable[] histMessages =
                    notification.extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
            messages.addAll(
                    Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages));
            for (Notification.MessagingStyle.Message message : messages) {
                uris.add(message.getDataUri());
            }
        }

        return uris;
    }

    public LogMaker getLogMaker(long now) {
        if (mLogMaker == null) {
            // initialize fields that only change on update (so a new record)
Loading