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

Commit 546aa10d authored by Beverly's avatar Beverly
Browse files

Track non-foreground service notifs for appOps

Previously we were only updating appOps for notifications with standard
layouts that were associated with a foreground services. However,
non-foreground service notifications can be tagged with appOps, so we
make sure to update these notifications' appOps whenever appOps are
changed. (We do this by tracking all notifications with standard
layouts instead of just the foreground service notifications)

Test: atest AppOpsCoordinatorTest ForegroundServiceControllerTest
Test: manual (use camera on whatsApp, receive message with app op, quit
app, see that app op is gone on the notification)
Fixes: 158585352
Change-Id: I674fc42441c2847a030df03516484ee6cd9217ac

Change-Id: I0527b8596277e53bea5a68aa57e0726aea9d14ac
parent 3fb94d8e
Loading
Loading
Loading
Loading
+28 −24
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.util.Assert;

import java.util.Set;

import javax.inject.Inject;
import javax.inject.Singleton;

@@ -62,7 +64,7 @@ public class ForegroundServiceController {

    /**
     * @return true if this user has services missing notifications and therefore needs a
     * disclosure notification.
     * disclosure notification for running a foreground service.
     */
    public boolean isDisclosureNeededForUser(int userId) {
        synchronized (mMutex) {
@@ -74,26 +76,26 @@ public class ForegroundServiceController {

    /**
     * @return true if this user/pkg has a missing or custom layout notification and therefore needs
     * a disclosure notification for system alert windows.
     * a disclosure notification showing the user which appsOps the app is using.
     */
    public boolean isSystemAlertWarningNeeded(int userId, String pkg) {
        synchronized (mMutex) {
            final ForegroundServicesUserState services = mUserServices.get(userId);
            if (services == null) return false;
            return services.getStandardLayoutKey(pkg) == null;
            return services.getStandardLayoutKeys(pkg) == null;
        }
    }

    /**
     * Returns the key of the foreground service from this package using the standard template,
     * if one exists.
     * Returns the keys for notifications from this package using the standard template,
     * if they exist.
     */
    @Nullable
    public String getStandardLayoutKey(int userId, String pkg) {
    public ArraySet<String> getStandardLayoutKeys(int userId, String pkg) {
        synchronized (mMutex) {
            final ForegroundServicesUserState services = mUserServices.get(userId);
            if (services == null) return null;
            return services.getStandardLayoutKey(pkg);
            return services.getStandardLayoutKeys(pkg);
        }
    }

@@ -140,20 +142,23 @@ public class ForegroundServiceController {
        }

        // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
        //  ForegroundCoordinator
        // Update appOp if there's an associated pending or visible notification:
        final String foregroundKey = getStandardLayoutKey(userId, packageName);
        if (foregroundKey != null) {
            final NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(foregroundKey);
        //  AppOpsCoordinator
        // Update appOps if there are associated pending or visible notifications
        final Set<String> notificationKeys = getStandardLayoutKeys(userId, packageName);
        if (notificationKeys != null) {
            boolean changed = false;
            for (String key : notificationKeys) {
                final NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(key);
                if (entry != null
                        && uid == entry.getSbn().getUid()
                        && packageName.equals(entry.getSbn().getPackageName())) {
                boolean changed;
                    synchronized (entry.mActiveAppOps) {
                        if (active) {
                        changed = entry.mActiveAppOps.add(appOpCode);
                            changed |= entry.mActiveAppOps.add(appOpCode);
                        } else {
                        changed = entry.mActiveAppOps.remove(appOpCode);
                            changed |= entry.mActiveAppOps.remove(appOpCode);
                        }
                    }
                }
            }
            if (changed) {
@@ -161,7 +166,6 @@ public class ForegroundServiceController {
            }
        }
    }
    }

    /**
     * Looks up the {@link ForegroundServicesUserState} for the given {@code userId}, then performs
+13 −13
Original line number Diff line number Diff line
@@ -163,6 +163,7 @@ public class ForegroundServiceNotificationListener {
                                userState.addImportantNotification(sbn.getPackageName(),
                                        sbn.getKey());
                            }
                        }
                        final Notification.Builder builder =
                                Notification.Builder.recoverBuilder(
                                        mContext, sbn.getNotification());
@@ -171,23 +172,22 @@ public class ForegroundServiceNotificationListener {
                                    sbn.getPackageName(), sbn.getKey());
                        }
                    }
                    }
                    tagForeground(entry);
                    tagAppOps(entry);
                    return true;
                },
                true /* create if not found */);
    }

    // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
    //  ForegroundCoordinator
    private void tagForeground(NotificationEntry entry) {
    //  AppOpsCoordinator
    private void tagAppOps(NotificationEntry entry) {
        final StatusBarNotification sbn = entry.getSbn();
        ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
                sbn.getUserId(),
                sbn.getPackageName());
        if (activeOps != null) {
        synchronized (entry.mActiveAppOps) {
            entry.mActiveAppOps.clear();
            if (activeOps != null) {
                entry.mActiveAppOps.addAll(activeOps);
            }
        }
+14 −4
Original line number Diff line number Diff line
@@ -30,9 +30,11 @@ public class ForegroundServicesUserState {

    private String[] mRunning = null;
    private long mServiceStartTime = 0;
    // package -> sufficiently important posted notification keys

    // package -> sufficiently important posted notification keys that signal an app is
    // running a foreground service
    private ArrayMap<String, ArraySet<String>> mImportantNotifications = new ArrayMap<>(1);
    // package -> standard layout posted notification keys
    // package -> standard layout posted notification keys that can display appOps
    private ArrayMap<String, ArraySet<String>> mStandardLayoutNotifications = new ArrayMap<>(1);

    // package -> app ops
@@ -110,6 +112,11 @@ public class ForegroundServicesUserState {
        return found;
    }

    /**
     * System disclosures for foreground services are required if an app has a foreground service
     * running AND the app hasn't posted its own notification signalling it is running a
     * foreground service
     */
    public boolean isDisclosureNeeded() {
        if (mRunning != null
                && System.currentTimeMillis() - mServiceStartTime
@@ -129,12 +136,15 @@ public class ForegroundServicesUserState {
        return mAppOps.get(pkg);
    }

    public String getStandardLayoutKey(String pkg) {
    /**
     * Gets the notifications with standard layouts associated with this package
     */
    public ArraySet<String> getStandardLayoutKeys(String pkg) {
        final ArraySet<String> set = mStandardLayoutNotifications.get(pkg);
        if (set == null || set.size() == 0) {
            return null;
        }
        return set.valueAt(0);
        return set;
    }

    @Override
+33 −28
Original line number Diff line number Diff line
@@ -39,8 +39,8 @@ import javax.inject.Inject;
import javax.inject.Singleton;

/**
 * Handles ForegroundService interactions with notifications.
 *  Tags notifications with appOps.
 * Handles ForegroundService and AppOp interactions with notifications.
 *  Tags notifications with appOps
 *  Lifetime extends notifications associated with an ongoing ForegroundService.
 *  Filters out notifications that represent foreground services that are no longer running
 *
@@ -48,12 +48,10 @@ import javax.inject.Singleton;
 *  frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceController
 *  frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener
 *  frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender
 *
 *  TODO: AppOps stuff should be spun off into its own coordinator
 */
@Singleton
public class ForegroundCoordinator implements Coordinator {
    private static final String TAG = "ForegroundCoordinator";
public class AppOpsCoordinator implements Coordinator {
    private static final String TAG = "AppOpsCoordinator";

    private final ForegroundServiceController mForegroundServiceController;
    private final AppOpsController mAppOpsController;
@@ -62,7 +60,7 @@ public class ForegroundCoordinator implements Coordinator {
    private NotifPipeline mNotifPipeline;

    @Inject
    public ForegroundCoordinator(
    public AppOpsCoordinator(
            ForegroundServiceController foregroundServiceController,
            AppOpsController appOpsController,
            @Main DelayableExecutor mainExecutor) {
@@ -89,18 +87,22 @@ public class ForegroundCoordinator implements Coordinator {
    }

    /**
     * Filters out notifications that represent foreground services that are no longer running.
     * Filters out notifications that represent foreground services that are no longer running or
     * that already have an app notification with the appOps tagged to
     */
    private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
        @Override
        public boolean shouldFilterOut(NotificationEntry entry, long now) {
            StatusBarNotification sbn = entry.getSbn();

            // Filters out system-posted disclosure notifications when unneeded
            if (mForegroundServiceController.isDisclosureNotification(sbn)
                    && !mForegroundServiceController.isDisclosureNeededForUser(
                            sbn.getUser().getIdentifier())) {
                return true;
            }

            // Filters out system alert notifications when unneeded
            if (mForegroundServiceController.isSystemAlertNotification(sbn)) {
                final String[] apps = sbn.getNotification().extras.getStringArray(
                        Notification.EXTRA_FOREGROUND_APPS);
@@ -179,23 +181,24 @@ public class ForegroundCoordinator implements Coordinator {
    private NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
        @Override
        public void onEntryAdded(NotificationEntry entry) {
            tagForeground(entry);
            tagAppOps(entry);
        }

        @Override
        public void onEntryUpdated(NotificationEntry entry) {
            tagForeground(entry);
            tagAppOps(entry);
        }

        private void tagForeground(NotificationEntry entry) {
        private void tagAppOps(NotificationEntry entry) {
            final StatusBarNotification sbn = entry.getSbn();
            // note: requires that the ForegroundServiceController is updating their appOps first
            ArraySet<Integer> activeOps =
                    mForegroundServiceController.getAppOps(
                            sbn.getUser().getIdentifier(),
                            sbn.getPackageName());
            if (activeOps != null) {

            entry.mActiveAppOps.clear();
            if (activeOps != null) {
                entry.mActiveAppOps.addAll(activeOps);
            }
        }
@@ -218,26 +221,28 @@ public class ForegroundCoordinator implements Coordinator {

        int userId = UserHandle.getUserId(uid);

        // Update appOp if there's an associated posted notification:
        final String foregroundKey = mForegroundServiceController.getStandardLayoutKey(userId,
                packageName);
        if (foregroundKey != null) {
            final NotificationEntry entry = findNotificationEntryWithKey(foregroundKey);
        // Update appOps of the app's posted notifications with standard layouts
        final ArraySet<String> notifKeys =
                mForegroundServiceController.getStandardLayoutKeys(userId, packageName);
        if (notifKeys != null) {
            boolean changed = false;
            for (int i = 0; i < notifKeys.size(); i++) {
                final NotificationEntry entry = findNotificationEntryWithKey(notifKeys.valueAt(i));
                if (entry != null
                        && uid == entry.getSbn().getUid()
                        && packageName.equals(entry.getSbn().getPackageName())) {
                boolean changed;
                    if (active) {
                    changed = entry.mActiveAppOps.add(code);
                        changed |= entry.mActiveAppOps.add(code);
                    } else {
                    changed = entry.mActiveAppOps.remove(code);
                        changed |= entry.mActiveAppOps.remove(code);
                    }
                }
            }
            if (changed) {
                mNotifFilter.invalidateList();
            }
        }
    }
    }

    private NotificationEntry findNotificationEntryWithKey(String key) {
        for (NotificationEntry entry : mNotifPipeline.getAllNotifs()) {
+2 −2
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ public class NotifCoordinators implements Dumpable {
            HideNotifsForOtherUsersCoordinator hideNotifsForOtherUsersCoordinator,
            KeyguardCoordinator keyguardCoordinator,
            RankingCoordinator rankingCoordinator,
            ForegroundCoordinator foregroundCoordinator,
            AppOpsCoordinator appOpsCoordinator,
            DeviceProvisionedCoordinator deviceProvisionedCoordinator,
            BubbleCoordinator bubbleCoordinator,
            HeadsUpCoordinator headsUpCoordinator,
@@ -64,7 +64,7 @@ public class NotifCoordinators implements Dumpable {
        mCoordinators.add(hideNotifsForOtherUsersCoordinator);
        mCoordinators.add(keyguardCoordinator);
        mCoordinators.add(rankingCoordinator);
        mCoordinators.add(foregroundCoordinator);
        mCoordinators.add(appOpsCoordinator);
        mCoordinators.add(deviceProvisionedCoordinator);
        mCoordinators.add(bubbleCoordinator);
        if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
Loading