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

Commit 478bb1d6 authored by Yuri Lin's avatar Yuri Lin Committed by Android (Google) Code Review
Browse files

Merge "Update adjustment preferences: add users + summarization" into main

parents 69020c74 43a2b417
Loading
Loading
Loading
Loading
+47 −23
Original line number Diff line number Diff line
@@ -13040,32 +13040,56 @@ public class NotificationManagerService extends SystemService {
        }
        /**
         * Fills out {@link BundlePreferences} proto and wraps it in a {@link StatsEvent}.
         * Fills out {@link NotificationAdjustmentPreferences} proto and wraps it in a
         * {@link StatsEvent}.
         */
        @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
        protected void pullAdjustmentPreferencesStats(List<StatsEvent> events) {
            boolean bundlesAllowed = true;
            final List<UserInfo> allUsers = mUm.getUsers();
            for (UserInfo ui : allUsers) {
                int userId = ui.getUserHandle().getIdentifier();
                boolean bundlesSupported, summariesSupported;
                synchronized (mLock) {
                    List<String> unsupportedAdjustments = new ArrayList(
                        mNasUnsupported.getOrDefault(
                                UserHandle.getUserId(Binder.getCallingUid()),
                            mNasUnsupported.getOrDefault(userId,
                                    new HashSet(List.of(mDefaultUnsupportedAdjustments)))
                    );
                bundlesAllowed = !unsupportedAdjustments.contains(Adjustment.KEY_TYPE);
                    bundlesSupported = !unsupportedAdjustments.contains(Adjustment.KEY_TYPE);
                    summariesSupported = !unsupportedAdjustments.contains(
                            Adjustment.KEY_SUMMARIZATION);
                }
            // TODO: b/411465430 - pull info for all users
            int[] allowedBundleTypes = getAllowedClassificationTypes(UserHandle.getCallingUserId());
                if (notificationClassificationUi() && bundlesSupported) {
                    boolean bundlesAllowed = isAdjustmentAllowed(userId, KEY_TYPE);
                    int[] allowedBundleTypes = getAllowedClassificationTypes(userId);
                    events.add(FrameworkStatsLog.buildStatsEvent(
                            NOTIFICATION_ADJUSTMENT_PREFERENCES,
                            /* optional int32 event_id = 1 */
                    NotificationPullStatsEvent.NOTIFICATION_BUNDLE_PREFERENCES_PULLED.getId(),
                            NotificationPullStatsEvent.NOTIFICATION_BUNDLE_PREFERENCES_PULLED
                                    .getId(),
                            /* optional bool adjustment_allowed = 2 */ bundlesAllowed,
                    /* repeated android.stats.notification.BundleTypes allowed_bundle_types = 3 */
                    allowedBundleTypes,
                            /* repeated BundleTypes allowed_bundle_types = 3 */ allowedBundleTypes,
                            /* optional android.stats.notification.AdjustmentKey key = 4 */
                    NotificationPullStatsEvent.adjustmentKeyEnum(KEY_TYPE)));
                            NotificationPullStatsEvent.adjustmentKeyEnum(KEY_TYPE),
                            /* optional int32 user_id = 5 */ userId));
                }
                if ((nmSummarization() || nmSummarizationUi()) && summariesSupported) {
                    boolean summariesAllowed = isAdjustmentAllowed(userId, KEY_SUMMARIZATION);
                    events.add(FrameworkStatsLog.buildStatsEvent(
                            NOTIFICATION_ADJUSTMENT_PREFERENCES,
                            /* optional int32 event_id = 1 */
                            NotificationPullStatsEvent.NOTIFICATION_SUMMARIZATION_PREFERENCES_PULLED
                                    .getId(),
                            /* optional bool adjustment_allowed = 2 */ summariesAllowed,
                            /* repeated BundleTypes allowed_bundle_types = 3 */ new int[]{},
                            /* optional android.stats.notification.AdjustmentKey key = 4 */
                            NotificationPullStatsEvent.adjustmentKeyEnum(KEY_SUMMARIZATION),
                            /* optional int32 user_id = 5 */ userId));
                }
            }
        }
    }
+3 −1
Original line number Diff line number Diff line
@@ -370,7 +370,9 @@ interface NotificationRecordLogger {

    enum NotificationPullStatsEvent implements UiEventLogger.UiEventEnum {
        @UiEvent(doc = "Notification Bundle Preferences pulled.")
        NOTIFICATION_BUNDLE_PREFERENCES_PULLED(2072);
        NOTIFICATION_BUNDLE_PREFERENCES_PULLED(2072),
        @UiEvent(doc = "Notification Summarization Preferences pulled.")
        NOTIFICATION_SUMMARIZATION_PREFERENCES_PULLED(2245);

        private final int mId;
        NotificationPullStatsEvent(int id) {
+88 −63
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ import com.android.internal.util.CollectionUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.os.AtomsProto;
import com.android.os.notification.BundleTypes;
import com.android.os.notification.NotificationAdjustmentPreferences;
import com.android.os.notification.NotificationExtensionAtoms;
import com.android.os.notification.NotificationProtoEnums;
@@ -97,9 +98,11 @@ import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@RunWith(AndroidJUnit4.class)
public class NotificationAssistantsTest extends UiServiceTestCase {
@@ -174,13 +177,14 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
                com.android.internal.R.string.config_defaultAssistantAccessComponent,
                mCn.flattenToString());
        mNm.mDefaultUnsupportedAdjustments = new String[] {};
        mAssistants = spy(mNm.new NotificationAssistants(mContext, mLock, mUserProfiles, miPm));
        when(mNm.getBinderService()).thenReturn(mINm);
        mContext.ensureTestableResources();

        LocalServices.removeServiceForTest(UserManagerInternal.class);
        LocalServices.addService(UserManagerInternal.class, mUmInternal);

        mAssistants = spy(mNm.new NotificationAssistants(mContext, mLock, mUserProfiles, miPm));
        when(mNm.getBinderService()).thenReturn(mINm);
        mContext.ensureTestableResources();

        List<ResolveInfo> approved = new ArrayList<>();
        ResolveInfo resolve = new ResolveInfo();
        approved.add(resolve);
@@ -197,6 +201,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
        users.add(mTen);
        users.add(new UserInfo(11, "11", 0));
        users.add(new UserInfo(12, "12", 0));
        users.add(new UserInfo(13, "13", 0));
        for (UserInfo user : users) {
            when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
        }
@@ -978,34 +983,94 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
    @Test
    @SuppressWarnings("GuardedBy")
    @EnableFlags({android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
            android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
            Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI, Flags.FLAG_NM_SUMMARIZATION,
            Flags.FLAG_NM_SUMMARIZATION_UI})
    public void testPullAdjustmentPreferencesStats_fillsOutStatsEvent()
            throws Exception {
        // Create the current user and enable the package
        int userId = ActivityManager.getCurrentUser();
        mAssistants.loadDefaultsFromConfig(true);
        mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
                true, true);
        ManagedServices.ManagedServiceInfo info =
                mAssistants.new ManagedServiceInfo(null, mCn, userId, false, null, 35, 2345256);

        // Ensure bundling is enabled
        mAssistants.setAdjustmentTypeSupportedState(info.userid, KEY_TYPE, true);
        // Enable these specific bundle types
        mAssistants.setAssistantClassificationTypeState(info.userid, TYPE_SOCIAL_MEDIA, false);
        mAssistants.setAssistantClassificationTypeState(info.userid, TYPE_PROMOTION, false);
        mAssistants.setAssistantClassificationTypeState(info.userid, TYPE_NEWS, true);
        mAssistants.setAssistantClassificationTypeState(info.userid, TYPE_CONTENT_RECOMMENDATION,
        // Scenario setup: the total list of users is 0, 10, 11 (profile of 0), 12, 13
        //   * user 0 has both bundles (KEY_TYPE) + summaries (KEY_SUMMARIZATION) supported
        //      * KEY_TYPE is allowed; KEY_SUMMARIZATION disallowed
        //   * user 13 has only KEY_TYPE, which is disallowed
        //   * other users have neither supported
        when(mUmInternal.getProfileParentId(13)).thenReturn(13);  // make sure 12 is a full user
        mAssistants.setAdjustmentTypeSupportedState(mZero.id, KEY_TYPE, true);
        mAssistants.allowAdjustmentType(mZero.id, KEY_TYPE);
        mAssistants.setAdjustmentTypeSupportedState(mZero.id, KEY_SUMMARIZATION, true);
        mAssistants.disallowAdjustmentType(mZero.id, KEY_SUMMARIZATION);
        mAssistants.setAdjustmentTypeSupportedState(13, KEY_TYPE, true);
        mAssistants.disallowAdjustmentType(13, KEY_TYPE);
        mAssistants.setAdjustmentTypeSupportedState(13, KEY_SUMMARIZATION, false);
        for (int user : List.of(mTen.id, 11, 12)) {
            mAssistants.setAdjustmentTypeSupportedState(user, KEY_TYPE, false);
            mAssistants.setAdjustmentTypeSupportedState(user, KEY_SUMMARIZATION, false);
        }

        // Enable specific bundle types for user 0
        mAssistants.setAssistantClassificationTypeState(mZero.id, TYPE_SOCIAL_MEDIA, false);
        mAssistants.setAssistantClassificationTypeState(mZero.id, TYPE_PROMOTION, false);
        mAssistants.setAssistantClassificationTypeState(mZero.id, TYPE_NEWS, true);
        mAssistants.setAssistantClassificationTypeState(mZero.id, TYPE_CONTENT_RECOMMENDATION,
                true);

        // and different ones for user 12
        mAssistants.setAssistantClassificationTypeState(13, TYPE_SOCIAL_MEDIA, true);
        mAssistants.setAssistantClassificationTypeState(13, TYPE_PROMOTION, false);
        mAssistants.setAssistantClassificationTypeState(13, TYPE_NEWS, false);
        mAssistants.setAssistantClassificationTypeState(13, TYPE_CONTENT_RECOMMENDATION,
                false);

        // When pullBundlePreferencesStats is run with the given preferences
        ArrayList<StatsEvent> events = new ArrayList<>();
        mAssistants.pullAdjustmentPreferencesStats(events);

        // The StatsEvent is filled out with the expected NotificationAdjustmentPreferences values.
        assertThat(events.size()).isEqualTo(1);
        AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(events.get(0));
        // We expect 2 atoms for user 0 and 1 atom for user 10.
        assertThat(events.size()).isEqualTo(3);

        // Collect all the resulting atoms in a map of user ID -> adjustment type (enum) -> atom to
        // confirm that we have the correct set of atoms without enforcing anything about ordering.
        Map<Integer, Map<Integer, NotificationAdjustmentPreferences>> atoms =
                new ArrayMap<>();
        for (StatsEvent event : events) {
            AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(event);
            NotificationAdjustmentPreferences p = parsePulledAtom(atom);
            int userId = p.getUserId();
            atoms.putIfAbsent(userId, new ArrayMap<>());
            atoms.get(userId).put(p.getKey().getNumber(), p);
        }

        // user 0, KEY_TYPE (bundles)
        assertThat(atoms).containsKey(mZero.id);
        Map<Integer, NotificationAdjustmentPreferences> userZeroAtoms = atoms.get(mZero.id);
        assertThat(userZeroAtoms).containsKey(NotificationProtoEnums.KEY_TYPE);
        NotificationAdjustmentPreferences p0b = userZeroAtoms.get(NotificationProtoEnums.KEY_TYPE);
        assertThat(p0b.getAdjustmentAllowed()).isTrue();
        assertThat(p0b.getAllowedBundleTypesList()).containsExactly(
                BundleTypes.forNumber(NotificationProtoEnums.TYPE_NEWS),
                BundleTypes.forNumber(NotificationProtoEnums.TYPE_CONTENT_RECOMMENDATION));

        // user 0, KEY_SUMMARIZATION
        assertThat(userZeroAtoms).containsKey(NotificationProtoEnums.KEY_SUMMARIZATION);
        NotificationAdjustmentPreferences p0s = userZeroAtoms.get(
                NotificationProtoEnums.KEY_SUMMARIZATION);
        assertThat(p0s.getAdjustmentAllowed()).isFalse();

        // user 12, KEY_TYPE (bundles)
        assertThat(atoms).containsKey(13);
        assertThat(atoms.get(13)).containsKey(NotificationProtoEnums.KEY_TYPE);
        NotificationAdjustmentPreferences p13b = atoms.get(13).get(
                NotificationProtoEnums.KEY_TYPE);
        assertThat(p13b.getAdjustmentAllowed()).isFalse();
        assertThat(p13b.getAllowedBundleTypesList())
                .containsExactly(BundleTypes.forNumber(NotificationProtoEnums.TYPE_SOCIAL_MEDIA));
    }

    // Helper function for getting the NotificationAdjustmentPreferences pulled atom data from a
    // given Atom object that's expected to have this extension.
    private NotificationAdjustmentPreferences parsePulledAtom(AtomsProto.Atom atom)
            throws IOException {
        // The returned atom does not have external extensions registered.
        // So we serialize and then deserialize with extensions registered.
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@@ -1015,50 +1080,10 @@ public class NotificationAssistantsTest extends UiServiceTestCase {

        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        CodedInputStream codedis = CodedInputStream.newInstance(inputStream);
        atom = AtomsProto.Atom.parseFrom(codedis, mRegistry);
        assertTrue(atom.hasExtension(NotificationExtensionAtoms.notificationAdjustmentPreferences));
        NotificationAdjustmentPreferences p =
                atom.getExtension(NotificationExtensionAtoms.notificationAdjustmentPreferences);
        assertThat(p.getAdjustmentAllowed()).isTrue();
        assertThat(p.getAllowedBundleTypes(0).getNumber())
                .isEqualTo(NotificationProtoEnums.TYPE_NEWS);
        assertThat(p.getAllowedBundleTypes(1).getNumber())
                .isEqualTo(NotificationProtoEnums.TYPE_CONTENT_RECOMMENDATION);

        // Disable the top-level bundling setting
        mAssistants.setAdjustmentTypeSupportedState(info.userid, KEY_TYPE, false);
        // Enable these specific bundle types
        mAssistants.setAssistantClassificationTypeState(info.userid, TYPE_PROMOTION, true);
        mAssistants.setAssistantClassificationTypeState(info.userid, TYPE_NEWS, false);
        mAssistants.setAssistantClassificationTypeState(info.userid, TYPE_CONTENT_RECOMMENDATION,
                true);

        ArrayList<StatsEvent> eventsDisabled = new ArrayList<>();
        mAssistants.pullAdjustmentPreferencesStats(eventsDisabled);

        // The StatsEvent is filled out with the expected NotificationAdjustmentPreferences values.
        assertThat(eventsDisabled.size()).isEqualTo(1);
        AtomsProto.Atom atomDisabled = StatsEventTestUtils.convertToAtom(eventsDisabled.get(0));

        // The returned atom does not have external extensions registered.
        // So we serialize and then deserialize with extensions registered.
        outputStream = new ByteArrayOutputStream();
        codedos = CodedOutputStream.newInstance(outputStream);
        atomDisabled.writeTo(codedos);
        codedos.flush();

        inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        codedis = CodedInputStream.newInstance(inputStream);
        atomDisabled = AtomsProto.Atom.parseFrom(codedis, mRegistry);
        assertTrue(atomDisabled.hasExtension(NotificationExtensionAtoms
                .notificationAdjustmentPreferences));

        NotificationAdjustmentPreferences p2 = atomDisabled.getExtension(
        AtomsProto.Atom parsedAtom = AtomsProto.Atom.parseFrom(codedis, mRegistry);
        assertTrue(parsedAtom.hasExtension(
                NotificationExtensionAtoms.notificationAdjustmentPreferences));
        return parsedAtom.getExtension(
                NotificationExtensionAtoms.notificationAdjustmentPreferences);
        assertThat(p2.getAdjustmentAllowed()).isFalse();
        assertThat(p2.getAllowedBundleTypes(0).getNumber())
                .isEqualTo(NotificationProtoEnums.TYPE_PROMOTION);
        assertThat(p2.getAllowedBundleTypes(1).getNumber())
                .isEqualTo(NotificationProtoEnums.TYPE_CONTENT_RECOMMENDATION);
    }
}
 No newline at end of file