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

Commit 4259fa31 authored by Yuri Lin's avatar Yuri Lin
Browse files

Add packages with denied adjustments to PackageNotificationPreferences

Adds a method to get packages with denied adjustments for each user, and includes that in the pulled atom. This replaces the previous now-defunct logging for permitted bundle types.

Bug: 409751981
Bug: 413434963
Test: PreferencesHelperTest, atom tester
Flag: android.app.notification_classification_ui

Change-Id: I4dfa6c13bdd5b8a318a7db0133485b72efe9cdd4
parent 7cfbf1fd
Loading
Loading
Loading
Loading
+25 −2
Original line number Diff line number Diff line
@@ -3170,7 +3170,7 @@ public class NotificationManagerService extends SystemService {
                if (notificationClassificationUi()) {
                    mPreferencesHelper.pullPackagePreferencesStats(data,
                            getAllUsersNotificationPermissions(),
                            new ArrayMap<>());
                            mAssistants.getDeniedKeysForUsersAndPackages());
                } else {
                    mPreferencesHelper.pullPackagePreferencesStats(data,
                            getAllUsersNotificationPermissions());
@@ -3183,7 +3183,8 @@ public class NotificationManagerService extends SystemService {
                mPreferencesHelper.pullPackageChannelGroupPreferencesStats(data);
                break;
            case NOTIFICATION_ADJUSTMENT_PREFERENCES:
                if (notificationClassification() && notificationClassificationUi()) {
                if ((notificationClassification() && notificationClassificationUi())
                        || nmSummarization() || nmSummarizationUi()) {
                    mAssistants.pullAdjustmentPreferencesStats(data);
                }
                break;
@@ -12462,6 +12463,28 @@ public class NotificationManagerService extends SystemService {
            }
        }
        // For logging preferences: get a map of user id -> package name -> list of denied keys
        // This is essentially a reconfiguration of the contents of mAdjustmentKeyDeniedPackages.
        @NonNull Map<Integer, Map<String, List<String>>> getDeniedKeysForUsersAndPackages() {
            Map<Integer, Map<String, List<String>>> out = new ArrayMap<>();
            synchronized (mLock) {
                for (int userId : mAdjustmentKeyDeniedPackages.keySet()) {
                    Map<String, Set<String>> pkgsByType = mAdjustmentKeyDeniedPackages.get(userId);
                    if (!pkgsByType.isEmpty()) {
                        out.putIfAbsent(userId, new ArrayMap<>());
                        Map<String, List<String>> pkgMapForUser = out.get(userId);
                        for (String keyType : pkgsByType.keySet()) {
                            for (String pkgName : pkgsByType.get(keyType)) {
                                pkgMapForUser.putIfAbsent(pkgName, new ArrayList<>());
                                pkgMapForUser.get(pkgName).add(keyType);
                            }
                        }
                    }
                }
            }
            return out;
        }
        protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) {
            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
                ArrayList<String> keys = new ArrayList<>(records.size());
+29 −29
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.server.notification;

import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.Flags.nmSummarization;
import static android.app.Flags.nmSummarizationUi;
import static android.app.Flags.notificationClassificationUi;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
import static android.app.NotificationChannel.NEWS_ID;
@@ -99,6 +101,7 @@ import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.notification.NotificationRecordLogger.NotificationPullStatsEvent;
import com.android.server.notification.PermissionHelper.PackagePermission;

import org.json.JSONArray;
@@ -114,6 +117,7 @@ import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -2566,7 +2570,7 @@ public class PreferencesHelper implements RankingConfig {
     */
    public void pullPackagePreferencesStats(List<StatsEvent> events,
            ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
        pullPackagePreferencesStats(events, pkgPermissions, new ArrayMap<String, Set<Integer>>());
        pullPackagePreferencesStats(events, pkgPermissions, new ArrayMap<>());
    }


@@ -2577,14 +2581,12 @@ public class PreferencesHelper implements RankingConfig {
     *                       where the first represents whether the notification permission was
     *                       granted to that package, and the second represents whether the
     *                       permission was user-set.
     * @param pkgAdjustmentKeyTypes A map of package names that are not allowed to have their
     *                                 notifications classified into differently typed notification
     *                                 channels, and the channels that they're allowed to be
     *                                 classified into.
     * @param adjustmentDeniedPackages A map of user id -> package name -> the set of adjustments
     *                                 that are not allowed for that package/user.
     */
    public void pullPackagePreferencesStats(List<StatsEvent> events,
            ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions,
            @NonNull Map<String, Set<Integer>> pkgAdjustmentKeyTypes) {
            @NonNull Map<Integer, Map<String, List<String>>> adjustmentDeniedPackages) {
        Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
        if (pkgPermissions != null) {
            pkgsWithPermissionsToHandle = pkgPermissions.keySet();
@@ -2630,13 +2632,14 @@ public class PreferencesHelper implements RankingConfig {
                        isFsiPermissionUserSet(r.pkg, r.uid, fsiState,
                                currentPermissionFlags);

                if (!notificationClassificationUi()
                        && pkgAdjustmentKeyTypes.keySet().size() > 0) {
                if (!(notificationClassificationUi() || nmSummarization() || nmSummarizationUi())
                        && adjustmentDeniedPackages.keySet().size() > 0) {
                    Slog.w(TAG, "Pkg adjustment types improperly allowed without flag set");
                }

                int[] allowedBundleTypes =
                        getAllowedTypesForPackage(pkgAdjustmentKeyTypes, r.pkg);
                int[] deniedAdjustmentsForPackage =
                        getDeniedAdjustmentsForPackage(adjustmentDeniedPackages,
                                UserHandle.getUserId(r.uid), r.pkg);

                events.add(FrameworkStatsLog.buildStatsEvent(
                        PACKAGE_NOTIFICATION_PREFERENCES,
@@ -2647,8 +2650,9 @@ public class PreferencesHelper implements RankingConfig {
                        /* optional bool user_set_importance = 5 */ importanceIsUserSet,
                        /* optional FsiState fsi_state = 6 */ fsiState,
                        /* optional bool is_fsi_permission_user_set = 7 */ fsiIsUserSet,
                        /* repeated int32 allowed_bundle_types = 8 */ allowedBundleTypes
                ));
                        /* repeated int32 allowed_bundle_types = 8 */ new int[]{},
                        /* repeated AdjustmentKey denied_adjustments = 9 */
                        deniedAdjustmentsForPackage));
            }
        }

@@ -2660,9 +2664,6 @@ public class PreferencesHelper implements RankingConfig {
                }
                pulledEvents++;

                int[] allowedBundleTypes =
                        getAllowedTypesForPackage(pkgAdjustmentKeyTypes, p.second);

                // Because all fields are required in FrameworkStatsLog.buildStatsEvent, we have
                // to fill in default values for all the unspecified fields.
                events.add(FrameworkStatsLog.buildStatsEvent(
@@ -2675,28 +2676,27 @@ public class PreferencesHelper implements RankingConfig {
                        /* optional bool user_set_importance = 5 */ pkgPermissions.get(p).second,
                        /* optional FsiState fsi_state = 6 */ 0,
                        /* optional bool is_fsi_permission_user_set = 7 */ false,
                        /* repeated BundleTypes allowed_bundle_types = 8 */ allowedBundleTypes));
                        /* repeated BundleTypes allowed_bundle_types = 8 */ new int[]{},
                        /* repeated AdjustmentKey denied_adjustments = 9 */ new int[]{}));
            }
        }
    }

    private int[] getAllowedTypesForPackage(@NonNull
                                            Map<String, Set<Integer>> pkgAdjustmentKeyTypes,
                                            String pkg) {
        int[] allowedBundleTypes = new int[]{};
    private int[] getDeniedAdjustmentsForPackage(
            @NonNull Map<Integer, Map<String, List<String>>> adjustmentDeniedPackages,
            @UserIdInt int userId, String pkg) {
        if (notificationClassificationUi()) {
            if (pkgAdjustmentKeyTypes.containsKey(pkg)) {
                // Convert from set to int[]
                Set<Integer> types = pkgAdjustmentKeyTypes.get(pkg);
                allowedBundleTypes = new int[types.size()];
                int i = 0;
                for (int val : types) {
                    allowedBundleTypes[i] = val;
                    i++;
            if (adjustmentDeniedPackages.containsKey(userId)) {
                List<String> deniedKeys = adjustmentDeniedPackages.get(userId).getOrDefault(pkg,
                        Collections.EMPTY_LIST);
                int[] out = new int[deniedKeys.size()];
                for (int i = 0; i < deniedKeys.size(); i++) {
                    out[i] = NotificationPullStatsEvent.adjustmentKeyEnum(deniedKeys.get(i));
                }
                return out;
            }
        }
        return allowedBundleTypes;
        return new int[]{};
    }

    /**
+62 −32
Original line number Diff line number Diff line
@@ -134,6 +134,7 @@ import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.service.notification.Adjustment;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.nano.RankingHelperProto;
import android.testing.TestableContentResolver;
@@ -6216,8 +6217,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {

        ArrayList<StatsEvent> events = new ArrayList<>();

        mHelper.pullPackagePreferencesStats(events, appPermissions,
                new ArrayMap<String, Set<Integer>>());
        mHelper.pullPackagePreferencesStats(events, appPermissions, new ArrayMap<>());

        // expected output. format: uid -> importance, as only uid (and not package name)
        // is in PackageNotificationPreferences
@@ -6253,6 +6253,13 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        NotificationChannel channelC = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
        mHelper.createNotificationChannel(PKG_P, UID_P, channelC, true, false, UID_P, false);

        // one app for a different user + one channel
        int otherUserId = UserHandle.getUserId(UID_P) + 1;
        int otherUid = UserHandle.getUid(otherUserId, UserHandle.getAppId(UID_P));
        NotificationChannel channelOther = new NotificationChannel("d", "d", IMPORTANCE_DEFAULT);
        mHelper.createNotificationChannel(PKG_P, otherUid, channelOther, true, false, otherUid,
                false);

        // build a collection of app permissions that should be passed in and used
        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions = new ArrayMap<>();
        pkgPermissions.put(new Pair<>(UID_N_MR1, PKG_N_MR1), new Pair<>(true, false));
@@ -6262,50 +6269,73 @@ public class PreferencesHelperTest extends UiServiceTestCase {
        mHelper.canShowBadge(PKG_O, UID_O);
        mHelper.canShowBadge(PKG_P, UID_P);

        // Sets bundles_allowed to true for these packages.
        ArrayMap<String, Set<Integer>> packageSpecificAdjustmentKeyTypes = new ArrayMap<>();
        Set<Integer> nMr1BundlesSet = new ArraySet<Integer>();
        nMr1BundlesSet.add(TYPE_NEWS);
        nMr1BundlesSet.add(TYPE_SOCIAL_MEDIA);
        packageSpecificAdjustmentKeyTypes.put(PKG_N_MR1, nMr1BundlesSet);
        Set<Integer> pBundlesSet = new ArraySet<Integer>();
        packageSpecificAdjustmentKeyTypes.put(PKG_P, pBundlesSet);
        // Sets denied types for packages.
        ArrayMap<Integer, Map<String, List<String>>> deniedAdjustments = new ArrayMap<>();
        ArrayMap<String, List<String>> deniedByPkg = new ArrayMap<>();
        List<String> nMr1DeniedAdjustments = new ArrayList<>();
        nMr1DeniedAdjustments.add(Adjustment.KEY_TYPE);
        deniedByPkg.put(PKG_N_MR1, nMr1DeniedAdjustments);
        List<String> pDeniedAdjustments = new ArrayList<>();
        pDeniedAdjustments.add(Adjustment.KEY_SUMMARIZATION);
        deniedByPkg.put(PKG_P, pDeniedAdjustments);
        deniedAdjustments.put(UserHandle.getUserId(UID_O), deniedByPkg);

        List<String> otherDeniedAdjustments = new ArrayList<>();
        otherDeniedAdjustments.add(Adjustment.KEY_TYPE);
        otherDeniedAdjustments.add(Adjustment.KEY_SUMMARIZATION);
        ArrayMap<String, List<String>> otherUserPackage = new ArrayMap<>();
        otherUserPackage.put(PKG_P, otherDeniedAdjustments);
        deniedAdjustments.put(otherUserId, otherUserPackage);

        ArrayList<StatsEvent> events = new ArrayList<>();

        mHelper.pullPackagePreferencesStats(events, pkgPermissions,
                packageSpecificAdjustmentKeyTypes);
                deniedAdjustments);

        assertEquals("total number of packages", 3, events.size());
        assertEquals("total number of packages", 4, events.size());

        // Convert resulting atoms into a map to confirm existence of logs independent of order,
        // verifying their type along the way.
        // Map is of uid -> associated atom
        Map<Integer, PackageNotificationPreferences> results = new ArrayMap<>();
        for (int i = 0; i < 4; i++) {
            AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(events.get(i));
            assertTrue(atom.hasPackageNotificationPreferences());
            PackageNotificationPreferences p = atom.getPackageNotificationPreferences();
            results.put(p.getUid(), p);
        }

        AtomsProto.Atom atom0 = StatsEventTestUtils.convertToAtom(events.get(0));
        assertTrue(atom0.hasPackageNotificationPreferences());
        PackageNotificationPreferences p0 = atom0.getPackageNotificationPreferences();
        assertThat(p0.getUid()).isEqualTo(UID_O);
        assertThat(results.containsKey(UID_O)).isTrue();
        PackageNotificationPreferences p0 = results.get(UID_O);
        assertThat(p0.getImportance()).isEqualTo(IMPORTANCE_NONE); // banned by permissions
        assertThat(p0.getUserSetImportance()).isTrue();
        assertThat(p0.getAllowedBundleTypesList()).hasSize(0);
        assertThat(p0.getDeniedAdjustmentsList()).hasSize(0);

        AtomsProto.Atom atom1 = StatsEventTestUtils.convertToAtom(events.get(1));
        assertTrue(atom1.hasPackageNotificationPreferences());
        PackageNotificationPreferences p1 = atom1.getPackageNotificationPreferences();
        assertThat(p1.getUid()).isEqualTo(UID_N_MR1);
        assertThat(results.containsKey(UID_N_MR1)).isTrue();
        PackageNotificationPreferences p1 = results.get(UID_N_MR1);
        assertThat(p1.getImportance()).isEqualTo(IMPORTANCE_DEFAULT);
        assertThat(p1.getUserSetImportance()).isFalse();
        assertThat(p1.getAllowedBundleTypesList()).hasSize(2);

        assertThat(p1.getAllowedBundleTypes(0).getNumber())
                .isEqualTo(NotificationProtoEnums.TYPE_SOCIAL_MEDIA);
        assertThat(p1.getAllowedBundleTypes(1).getNumber())
                .isEqualTo(NotificationProtoEnums.TYPE_NEWS);
        assertThat(p1.getDeniedAdjustmentsList()).hasSize(1);
        assertThat(p1.getDeniedAdjustments(0).getNumber()).isEqualTo(
                NotificationProtoEnums.KEY_TYPE);

        AtomsProto.Atom atom2 = StatsEventTestUtils.convertToAtom(events.get(2));
        assertTrue(atom2.hasPackageNotificationPreferences());
        PackageNotificationPreferences p2 = atom2.getPackageNotificationPreferences();
        assertThat(p2.getUid()).isEqualTo(UID_P);
        assertThat(results.containsKey(UID_P)).isTrue();
        PackageNotificationPreferences p2 = results.get(UID_P);
        assertThat(p2.getImportance()).isEqualTo(IMPORTANCE_UNSPECIFIED); // default: unspecified
        assertThat(p2.getUserSetImportance()).isFalse();
        assertThat(p2.getAllowedBundleTypesList()).hasSize(0);
        assertThat(p2.getDeniedAdjustmentsList()).hasSize(1);
        assertThat(p2.getDeniedAdjustments(0).getNumber()).isEqualTo(
                NotificationProtoEnums.KEY_SUMMARIZATION);

        assertThat(results.containsKey(otherUid)).isTrue();
        PackageNotificationPreferences p3 = results.get(otherUid);
        assertThat(p3.getImportance()).isEqualTo(IMPORTANCE_UNSPECIFIED); // default: unspecified
        assertThat(p3.getUserSetImportance()).isFalse();
        assertThat(p3.getDeniedAdjustmentsList()).hasSize(2);
        assertThat(p3.getDeniedAdjustments(0).getNumber()).isEqualTo(
                NotificationProtoEnums.KEY_TYPE);
        assertThat(p3.getDeniedAdjustments(1).getNumber()).isEqualTo(
                NotificationProtoEnums.KEY_SUMMARIZATION);
    }

    @Test