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

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

Merge changes I38846241,Ie2e27503 into main

* changes:
  Only log adjustment preferences for full users and managed profiles
  Default managed profiles to off on KEY_TYPE adjustments only
parents 4b0c3c69 594efc70
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -280,6 +280,10 @@ abstract public class ManagedServices {
        }
    }

    UserProfiles getUserProfiles() {
        return mUserProfiles;
    }

    /**
     * When resetting a package, we need to enable default components that belong to that packages
     * we also need to disable components that are not default to return the managed service state
@@ -2340,6 +2344,30 @@ abstract public class ManagedServices {
            }
        }

        boolean isManagedProfileUser(int userId) {
            synchronized (mCurrentProfiles) {
                UserInfo user = mCurrentProfiles.get(userId);
                if (user == null) {
                    return false;
                }
                return user.isManagedProfile();
            }
        }

        int getProfileParentId(int userId, Context context) {
            final long identity = Binder.clearCallingIdentity();
            try {
                UserManager um = context.getSystemService(UserManager.class);
                UserInfo parent = um.getProfileParent(userId);
                if (parent != null) {
                    return parent.id;
                }
                return userId;  // if no parent, return itself
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
        }

        boolean hasParent(UserInfo profile, Context context) {
            final long identity = Binder.clearCallingIdentity();
            try {
+64 −14
Original line number Diff line number Diff line
@@ -12230,6 +12230,9 @@ public class NotificationManagerService extends SystemService {
        private static final String TAG_ENABLED_TYPES = "enabled_classification_types";
        private static final String ATT_NAS_UNSUPPORTED = "unsupported_adjustments";
        private static final String ATT_USER_ID = "user";
        // for classification only, but named a bit more generally in case this ever gets expanded
        private static final String TAG_SET_BY_USERS = "adjustment_pref_set_by_users";
        private static final String ATT_USER_LIST = "users";
        private final Object mLock = new Object();
@@ -12247,6 +12250,12 @@ public class NotificationManagerService extends SystemService {
        @GuardedBy("mLock")
        private Map<Integer, HashSet<String>> mNasUnsupported = new ArrayMap<>();
        // Set of user IDs for which the classification setting was ever explicitly changed (in
        // other words, the current setting -- allowed or disallowed -- is not default). Used for
        // handling default behavior for profiles until the user sets a preference.
        @GuardedBy("mLock")
        private Set<Integer> mClassificationPrefSetByUsers = new ArraySet<>();
        // Map of user ID -> the disallowed packages for each adjustment key.
        // Inner map key: Adjustment key. value - list of pkgs that we shouldn't apply
        // adjustments with that key to
@@ -12419,18 +12428,28 @@ public class NotificationManagerService extends SystemService {
        // be effectively denied for that user. In particular:
        // - if an adjustment is denied for that profile user's parent, then it is also effectively
        //   denied for that profile regardless of what the profile's current setting is.
        // - for classification (KEY_TYPE) only, if a user hasn't explicitly enabled the adjustment
        //   for their managed/work profile, default the setting to off.
        @GuardedBy("mLock")
        private @NonNull Set<String> deniedAdjustmentsForUser(@UserIdInt int userId) {
            Set<String> denied = new HashSet<>();
            if (mDeniedAdjustments.containsKey(userId)) {
                denied.addAll(mDeniedAdjustments.get(userId));
            }
            final @UserIdInt int parentId = mUmInternal.getProfileParentId(userId);
            if ((parentId != userId) && mDeniedAdjustments.containsKey(parentId)) {
            if (getUserProfiles().isProfileUser(userId, mContext)) {
                final @UserIdInt int parentId = getUserProfiles().getProfileParentId(userId,
                        mContext);
                if (mDeniedAdjustments.containsKey(parentId)) {
                    denied.addAll(mDeniedAdjustments.get(parentId));
                }
            // TODO: b/415768865 - add any cases where a (work/managed) profile should default to
            //                     off unless explicitly turned on
                // Managed profiles only: if the setting hasn't been explicitly set for this
                // profile, then also consider KEY_TYPE (classification) denied.
                if (getUserProfiles().isManagedProfileUser(userId)
                        && !mClassificationPrefSetByUsers.contains(userId)) {
                    denied.add(KEY_TYPE);
                }
            }
            return denied;
        }
@@ -12934,7 +12953,7 @@ public class NotificationManagerService extends SystemService {
            return Log.isLoggable("notification_assistant", Log.VERBOSE);
        }
        @GuardedBy("mNotificationLock")
        @GuardedBy("mLock")
        private void addDefaultClassificationTypes(int userId) {
            // Add the default classification types if the list is empty or not present.
            // Will do so for the profile's parent if the user ID is a profile user.
@@ -12953,16 +12972,19 @@ public class NotificationManagerService extends SystemService {
            if (!android.service.notification.Flags.notificationClassification()) {
                return;
            }
            synchronized (mLock) {
                if (mDeniedAdjustments.containsKey(userId)) {
                    mDeniedAdjustments.get(userId).remove(key);
                }
            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
                mHandler.post(() -> notifyCapabilitiesChanged(info));
            }
                if (KEY_TYPE.equals(key)) {
                    mClassificationPrefSetByUsers.add(userId);
                    addDefaultClassificationTypes(userId);
                }
            }
            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
                mHandler.post(() -> notifyCapabilitiesChanged(info));
            }
        }
        @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
        @GuardedBy("mNotificationLock")
@@ -12970,8 +12992,13 @@ public class NotificationManagerService extends SystemService {
            if (!android.service.notification.Flags.notificationClassification()) {
                return;
            }
            synchronized (mLock) {
                mDeniedAdjustments.putIfAbsent(userId, new ArraySet<>());
                mDeniedAdjustments.get(userId).add(key);
                if (KEY_TYPE.equals(key)) {
                    mClassificationPrefSetByUsers.add(userId);
                }
            }
            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
                mHandler.post(() -> notifyCapabilitiesChanged(info));
            }
@@ -13084,6 +13111,11 @@ public class NotificationManagerService extends SystemService {
                            TextUtils.join(",", mAllowedClassificationTypes.get(user)));
                    out.endTag(null, TAG_ENABLED_TYPES);
                }
                out.startTag(null, TAG_SET_BY_USERS);
                out.attribute(null, ATT_USER_LIST,
                        TextUtils.join(",", mClassificationPrefSetByUsers));
                out.endTag(null, TAG_SET_BY_USERS);
            }
        }
@@ -13120,7 +13152,7 @@ public class NotificationManagerService extends SystemService {
                            try {
                                userAllowedTypes.add(Integer.parseInt(type));
                            } catch (NumberFormatException e) {
                                Slog.wtf(TAG, "Bad type specified", e);
                                Slog.wtf(TAG, "Bad integer specified", e);
                            }
                        }
                        mAllowedClassificationTypes.put(user, userAllowedTypes);
@@ -13138,6 +13170,18 @@ public class NotificationManagerService extends SystemService {
                    userDeniedPackages.put(key, new ArraySet<>(pkgList));
                    mAdjustmentKeyDeniedPackages.put(user, userDeniedPackages);
                }
            } else if (TAG_SET_BY_USERS.equals(tag)) {
                final String users = XmlUtils.readStringAttribute(parser, ATT_USER_LIST);
                if (!TextUtils.isEmpty(users)) {
                    for (String userIdString : Arrays.asList(users.split(","))) {
                        try {
                            mClassificationPrefSetByUsers.add(
                                    Integer.parseInt(userIdString));
                        } catch (NumberFormatException e) {
                            Slog.wtf(TAG, "Bad type specified", e);
                        }
                    }
                }
            }
        }
@@ -13158,6 +13202,12 @@ public class NotificationManagerService extends SystemService {
        protected void pullAdjustmentPreferencesStats(List<StatsEvent> events) {
            final List<UserInfo> allUsers = mUm.getUsers();
            for (UserInfo ui : allUsers) {
                // only log for full users and managed profiles, which are the only users that have
                // separate switches available in settings
                if (!(ui.isFull() || ui.isManagedProfile())) {
                    continue;
                }
                int userId = ui.getUserHandle().getIdentifier();
                boolean bundlesSupported, summariesSupported;
+91 −27
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
package com.android.server.notification;

import static android.os.UserHandle.USER_ALL;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
import static android.service.notification.Adjustment.KEY_TYPE;
@@ -131,9 +132,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
    private TestableContext mContext = spy(getContext());
    Object mLock = new Object();


    UserInfo mZero = new UserInfo(0, "zero", 0);
    UserInfo mTen = new UserInfo(10, "ten", 0);
    UserInfo mZero = new UserInfo(0, "zero", UserInfo.FLAG_FULL);
    UserInfo mTen = new UserInfo(10, "ten", UserInfo.FLAG_PROFILE);

    ComponentName mCn = new ComponentName("a", "b");

@@ -199,11 +199,19 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
        List<UserInfo> users = new ArrayList<>();
        users.add(mZero);
        users.add(mTen);
        users.add(new UserInfo(11, "11", 0));
        users.add(new UserInfo(12, "12", 0));
        users.add(new UserInfo(13, "13", 0));
        users.add(new UserInfo(11, "11", null, UserInfo.FLAG_PROFILE, USER_TYPE_PROFILE_MANAGED));
        users.add(new UserInfo(12, "12", UserInfo.FLAG_PROFILE));
        users.add(new UserInfo(13, "13", UserInfo.FLAG_FULL));
        for (UserInfo user : users) {
            when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
            if (user.isProfile()) {
                when(mNm.hasParent(user)).thenReturn(true);
                when(mNm.isProfileUser(user)).thenReturn(true);
                when(mUserProfiles.isProfileUser(eq(user.id), any())).thenReturn(true);
                if (user.isManagedProfile()) {
                    when(mUserProfiles.isManagedProfileUser(user.id)).thenReturn(true);
                }
            }
        }
        when(mUm.getUsers()).thenReturn(users);
        when(mUm.getAliveUsers()).thenReturn(users);
@@ -213,8 +221,10 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
        profileIds.add(10);
        profileIds.add(12);
        when(mUmInternal.getProfileParentId(13)).thenReturn(13);  // 13 is a full user
        when(mUserProfiles.getProfileParentId(eq(13), any())).thenReturn(13);
        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
        when(mUmInternal.getProfileParentId(11)).thenReturn(mZero.id);
        when(mUserProfiles.getProfileParentId(eq(11), any())).thenReturn(mZero.id);
        when(mNm.isNASMigrationDone(anyInt())).thenReturn(true);
        when(mNm.canUseManagedServices(any(), anyInt(), any())).thenReturn(true);
        mRegistry = ExtensionRegistryLite.newInstance();
@@ -704,11 +714,13 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
        // if the profile's parent has that adjustment disabled.
        // User 11 is set up as a profile user of mZero in setup; user 13 is not
        mAssistants.allowAdjustmentType(11, Adjustment.KEY_TYPE);
        mAssistants.allowAdjustmentType(13, Adjustment.KEY_TYPE);
        mAssistants.disallowAdjustmentType(mZero.id, Adjustment.KEY_TYPE);

        assertThat(mAssistants.getAllowedAssistantAdjustments(11)).doesNotContain(
                Adjustment.KEY_TYPE);
        assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isFalse();
        assertThat(mAssistants.isAdjustmentAllowed(13, Adjustment.KEY_TYPE)).isTrue();

        // Now turn it back on for the parent; it should be considered allowed for the profile
        // (for which it was already on).
@@ -717,6 +729,21 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
        assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isTrue();
    }

    @Test
    public void testClassificationAdjustment_managedProfileDefaultsOff() {
        // Turn on KEY_TYPE classification for mZero (parent) but not 11 (managed profile)
        mAssistants.allowAdjustmentType(mZero.id, Adjustment.KEY_TYPE);
        assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isFalse();

        // Check this doesn't apply to other adjustments if they default to allowed
        mAssistants.allowAdjustmentType(mZero.id, Adjustment.KEY_SUMMARIZATION);
        assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_SUMMARIZATION)).isTrue();

        // now turn on classification for the profile user directly
        mAssistants.allowAdjustmentType(11, Adjustment.KEY_TYPE);
        assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isTrue();
    }

    @Test
    @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
    public void testAllowAdjustmentType_classifListEmpty_resetDefaultClassificationTypes() {
@@ -747,6 +774,28 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
            .containsExactly(TYPE_NEWS);
    }

    @Test
    @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
    public void testClassificationAdjustments_readWriteXml_userSetStateMaintained()
            throws Exception {
        // Turn on KEY_TYPE classification for mZero (parent) but not 11 (managed profile)
        mAssistants.allowAdjustmentType(mZero.id, Adjustment.KEY_TYPE);
        assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isFalse();

        // reload from XML; default state should persist
        writeXmlAndReload(USER_ALL);

        assertThat(mAssistants.isAdjustmentAllowed(mZero.id, Adjustment.KEY_TYPE)).isTrue();
        assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isFalse();

        mAssistants.allowAdjustmentType(11, Adjustment.KEY_TYPE);
        assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isTrue();

        // now that it's been set, we should still retain this information after XML reload
        writeXmlAndReload(USER_ALL);
        assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
    public void testDisallowAdjustmentType_readWriteXml_entries() throws Exception {
@@ -1009,22 +1058,30 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
            throws Exception {
        mAssistants.loadDefaultsFromConfig(true);

        // Scenario setup: the total list of users is 0, 10, 11 (profile of 0), 12, 13
        // Scenario setup: the total list of users is 0, 10, 11 (managed 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
        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);
        //      * KEY_TYPE is disallowed; KEY_SUMMARIZATION allowed
        //   * user 13 has only KEY_TYPE, which is allowed
        //   * user 11 (managed profile) has only KEY_SUMMARIZATION, disallowed
        //   * other users are non-managed profiles; should not be logged even if both types are
        //     supported
        for (int user : List.of(mZero.id, mTen.id, 12, 13)) {
            // users with KEY_TYPE supported: all but 11
            mAssistants.setAdjustmentTypeSupportedState(user, KEY_TYPE, true);
        }
        mAssistants.setAdjustmentTypeSupportedState(11, KEY_TYPE, false);

        for (int user : List.of(mZero.id, mTen.id, 11, 12)) {
            // users with KEY_SUMMARIZATION supported: all but 13
            mAssistants.setAdjustmentTypeSupportedState(user, KEY_SUMMARIZATION, true);
        }
        mAssistants.setAdjustmentTypeSupportedState(13, KEY_SUMMARIZATION, false);

        // permissions for adjustments as described above
        mAssistants.disallowAdjustmentType(mZero.id, KEY_TYPE);
        mAssistants.allowAdjustmentType(mZero.id, KEY_SUMMARIZATION);
        mAssistants.disallowAdjustmentType(11, KEY_SUMMARIZATION);
        mAssistants.allowAdjustmentType(13, KEY_TYPE);

        // Enable specific bundle types for user 0
        mAssistants.setAssistantClassificationTypeState(mZero.id, TYPE_SOCIAL_MEDIA, false);
@@ -1033,7 +1090,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
        mAssistants.setAssistantClassificationTypeState(mZero.id, TYPE_CONTENT_RECOMMENDATION,
                true);

        // and different ones for user 12
        // and different ones for user 13
        mAssistants.setAssistantClassificationTypeState(13, TYPE_SOCIAL_MEDIA, true);
        mAssistants.setAssistantClassificationTypeState(13, TYPE_PROMOTION, false);
        mAssistants.setAssistantClassificationTypeState(13, TYPE_NEWS, false);
@@ -1045,8 +1102,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
        mAssistants.pullAdjustmentPreferencesStats(events);

        // The StatsEvent is filled out with the expected NotificationAdjustmentPreferences values.
        // We expect 2 atoms for user 0 and 1 atom for user 10.
        assertThat(events.size()).isEqualTo(3);
        // We expect 2 atoms for user 0, 1 atom for user 11, and 1 for user 13.
        assertThat(events.size()).isEqualTo(4);

        // 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.
@@ -1065,7 +1122,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
        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.getAdjustmentAllowed()).isFalse();
        assertThat(p0b.getAllowedBundleTypesList()).containsExactly(
                BundleTypes.forNumber(NotificationProtoEnums.TYPE_NEWS),
                BundleTypes.forNumber(NotificationProtoEnums.TYPE_CONTENT_RECOMMENDATION));
@@ -1074,14 +1131,21 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
        assertThat(userZeroAtoms).containsKey(NotificationProtoEnums.KEY_SUMMARIZATION);
        NotificationAdjustmentPreferences p0s = userZeroAtoms.get(
                NotificationProtoEnums.KEY_SUMMARIZATION);
        assertThat(p0s.getAdjustmentAllowed()).isFalse();
        assertThat(p0s.getAdjustmentAllowed()).isTrue();

        // user 11, KEY_SUMMARIZATION
        assertThat(atoms).containsKey(11);
        assertThat(atoms.get(11)).containsKey(NotificationProtoEnums.KEY_SUMMARIZATION);
        NotificationAdjustmentPreferences p11s = atoms.get(11).get(
                NotificationProtoEnums.KEY_SUMMARIZATION);
        assertThat(p11s.getAdjustmentAllowed()).isFalse();

        // user 12, KEY_TYPE (bundles)
        // user 13, 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.getAdjustmentAllowed()).isTrue();
        assertThat(p13b.getAllowedBundleTypesList())
                .containsExactly(BundleTypes.forNumber(NotificationProtoEnums.TYPE_SOCIAL_MEDIA));
    }