From 756a475be511807adaf42fdf4ed3188ad9349f39 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 10 May 2022 16:37:29 -0400 Subject: [PATCH] Fix blockable system notifications Notifications can be non-blockable if their app has the permission fixed or if the app holds certain roles. Calculate that on device boot and role change, and use the same source of truth for all UIs. As a bonus, we can remove some systemui binder calls. Test: SystemUiTests & FrameworkUiServicesTests TesT: verify that 'usb debugging connected' notifs are non blockable but magnification ones are, in systemui and settings Fixes: 231662091 Change-Id: I980718ab61196901d976f9ea8ee035eafc021fc8 --- core/api/test-current.txt | 1 - .../android/app/INotificationManager.aidl | 1 + .../java/android/app/NotificationChannel.java | 21 +-- .../row/ExpandableNotificationRow.java | 66 ++------- .../row/ExpandableNotificationRowTest.java | 11 ++ .../NotificationManagerService.java | 7 + .../notification/PreferencesHelper.java | 68 +++++---- .../notification/PreferencesHelperTest.java | 140 ++++++++++++++++++ 8 files changed, 215 insertions(+), 100 deletions(-) diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 36e1c941cbc63..fe99c71d9a9a3 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -320,7 +320,6 @@ package android.app { method public void setDemoted(boolean); method public void setFgServiceShown(boolean); method public void setImportanceLockedByCriticalDeviceFunction(boolean); - method public void setImportanceLockedByOEM(boolean); method public void setImportantConversation(boolean); method public void setOriginalImportance(int); } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index df9f2a3cbb252..da6a551175e32 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -77,6 +77,7 @@ interface INotificationManager boolean areNotificationsEnabledForPackage(String pkg, int uid); boolean areNotificationsEnabled(String pkg); int getPackageImportance(String pkg); + boolean isImportanceLocked(String pkg, int uid); List getAllowedAssistantAdjustments(String pkg); void allowAssistantAdjustment(String adjustmentType); diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 6f0b03aeb6f32..c9cc1a179102a 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -253,7 +253,6 @@ public final class NotificationChannel implements Parcelable { // If this is a blockable system notification channel. private boolean mBlockableSystem = false; private int mAllowBubbles = DEFAULT_ALLOW_BUBBLE; - private boolean mImportanceLockedByOEM; private boolean mImportanceLockedDefaultApp; private String mParentId = null; private String mConversationId = null; @@ -322,13 +321,13 @@ public final class NotificationChannel implements Parcelable { mLightColor = in.readInt(); mBlockableSystem = in.readBoolean(); mAllowBubbles = in.readInt(); - mImportanceLockedByOEM = in.readBoolean(); mOriginalImportance = in.readInt(); mParentId = in.readString(); mConversationId = in.readString(); mDemoted = in.readBoolean(); mImportantConvo = in.readBoolean(); mDeletedTime = in.readLong(); + mImportanceLockedDefaultApp = in.readBoolean(); } @Override @@ -382,13 +381,13 @@ public final class NotificationChannel implements Parcelable { dest.writeInt(mLightColor); dest.writeBoolean(mBlockableSystem); dest.writeInt(mAllowBubbles); - dest.writeBoolean(mImportanceLockedByOEM); dest.writeInt(mOriginalImportance); dest.writeString(mParentId); dest.writeString(mConversationId); dest.writeBoolean(mDemoted); dest.writeBoolean(mImportantConvo); dest.writeLong(mDeletedTime); + dest.writeBoolean(mImportanceLockedDefaultApp); } /** @@ -847,14 +846,6 @@ public final class NotificationChannel implements Parcelable { return mBlockableSystem; } - /** - * @hide - */ - @TestApi - public void setImportanceLockedByOEM(boolean locked) { - mImportanceLockedByOEM = locked; - } - /** * @hide */ @@ -1107,8 +1098,8 @@ public final class NotificationChannel implements Parcelable { out.attributeBoolean(null, ATT_IMP_CONVERSATION, isImportantConversation()); } - // mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of - // truth and so aren't written to this xml file + // mImportanceLockedDefaultApp has a different source of truth and so isn't written to + // this xml file out.endTag(null, TAG_CHANNEL); } @@ -1251,7 +1242,6 @@ public final class NotificationChannel implements Parcelable { && Arrays.equals(mVibration, that.mVibration) && Objects.equals(getGroup(), that.getGroup()) && Objects.equals(getAudioAttributes(), that.getAudioAttributes()) - && mImportanceLockedByOEM == that.mImportanceLockedByOEM && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp && mOriginalImportance == that.mOriginalImportance && Objects.equals(getParentChannelId(), that.getParentChannelId()) @@ -1267,7 +1257,7 @@ public final class NotificationChannel implements Parcelable { getUserLockedFields(), isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getDeletedTimeMs(), getGroup(), getAudioAttributes(), isBlockable(), mAllowBubbles, - mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance, + mImportanceLockedDefaultApp, mOriginalImportance, mParentId, mConversationId, mDemoted, mImportantConvo); result = 31 * result + Arrays.hashCode(mVibration); return result; @@ -1312,7 +1302,6 @@ public final class NotificationChannel implements Parcelable { + ", mAudioAttributes=" + mAudioAttributes + ", mBlockableSystem=" + mBlockableSystem + ", mAllowBubbles=" + mAllowBubbles - + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp + ", mOriginalImp=" + mOriginalImportance + ", mParent=" + mParentId diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index ed69e0609096e..f975799b4b857 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -60,6 +60,7 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.util.MathUtils; import android.util.Property; +import android.util.Slog; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -363,9 +364,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Nullable private OnExpansionChangedListener mExpansionChangedListener; @Nullable private Runnable mOnIntrinsicHeightReachedRunnable; - private SystemNotificationAsyncTask mSystemNotificationAsyncTask = - new SystemNotificationAsyncTask(); - private float mTopRoundnessDuringLaunchAnimation; private float mBottomRoundnessDuringLaunchAnimation; @@ -516,46 +514,21 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mRowContentBindStage.requestRebind(mEntry, null /* callback */); } - /** - * Caches whether or not this row contains a system notification. Note, this is only cached - * once per notification as the packageInfo can't technically change for a notification row. - */ - private void cacheIsSystemNotification() { - //TODO: This probably shouldn't be in ExpandableNotificationRow - if (mEntry != null && mEntry.mIsSystemNotification == null) { - if (mSystemNotificationAsyncTask.getStatus() == AsyncTask.Status.PENDING) { - // Run async task once, only if it hasn't already been executed. Note this is - // executed in serial - no need to parallelize this small task. - mSystemNotificationAsyncTask.execute(); - } - } - } - /** * Returns whether this row is considered non-blockable (i.e. it's a non-blockable system notif * or is in an allowList). */ public boolean getIsNonblockable() { - // If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once - // again, but in-place on the main thread this time. This should rarely ever get called. - if (mEntry != null && mEntry.mIsSystemNotification == null) { - if (DEBUG) { - Log.d(TAG, "Retrieving isSystemNotification on main thread"); - } - mSystemNotificationAsyncTask.cancel(true /* mayInterruptIfRunning */); - mEntry.mIsSystemNotification = isSystemNotification(mContext, mEntry.getSbn()); + if (mEntry == null || mEntry.getChannel() == null) { + Log.w(TAG, "missing entry or channel"); + return true; + } + if (mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction() + && !mEntry.getChannel().isBlockable()) { + return true; } - boolean isNonblockable = mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction(); - - if (!isNonblockable && mEntry != null && mEntry.mIsSystemNotification != null) { - if (mEntry.mIsSystemNotification) { - if (mEntry.getChannel() != null && !mEntry.getChannel().isBlockable()) { - isNonblockable = true; - } - } - } - return isNonblockable; + return false; } private boolean isConversation() { @@ -1626,8 +1599,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mBubblesManagerOptional = bubblesManagerOptional; mNotificationGutsManager = gutsManager; mMetricsLogger = metricsLogger; - - cacheIsSystemNotification(); } private void initDimens() { @@ -3545,25 +3516,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView }); } - /** - * Background task for executing IPCs to check if the notification is a system notification. The - * output is used for both the blocking helper and the notification info. - */ - private class SystemNotificationAsyncTask extends AsyncTask { - - @Override - protected Boolean doInBackground(Void... voids) { - return isSystemNotification(mContext, mEntry.getSbn()); - } - - @Override - protected void onPostExecute(Boolean result) { - if (mEntry != null) { - mEntry.mIsSystemNotification = result; - } - } - } - private void setTargetPoint(Point p) { mTargetPoint = p; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index b3c34c1e4ff4e..1f9af81d6508f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -322,10 +322,21 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { ExpandableNotificationRow row = mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification()); row.getEntry().getChannel().setImportanceLockedByCriticalDeviceFunction(true); + row.getEntry().getChannel().setBlockable(false); assertTrue(row.getIsNonblockable()); } + @Test + public void testGetIsNonblockable_criticalDeviceFunction_butBlockable() throws Exception { + ExpandableNotificationRow row = + mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification()); + row.getEntry().getChannel().setImportanceLockedByCriticalDeviceFunction(true); + row.getEntry().getChannel().setBlockable(true); + + assertFalse(row.getIsNonblockable()); + } + @Test public void testCanDismissNoClear() throws Exception { ExpandableNotificationRow row = diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 9042326c57607..b6376a95f88e3 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2284,6 +2284,7 @@ public class NotificationManagerService extends SystemService { mNotificationChannelLogger, mAppOps, new SysUiStatsEvent.BuilderFactory()); + mPreferencesHelper.updateFixedImportance(mUm.getUsers()); mRankingHelper = new RankingHelper(getContext(), mRankingHandler, mPreferencesHelper, @@ -3631,6 +3632,12 @@ public class NotificationManagerService extends SystemService { } } + @Override + public boolean isImportanceLocked(String pkg, int uid) { + checkCallerIsSystem(); + return mPreferencesHelper.isImportanceLocked(pkg, uid); + } + @Override public boolean canShowBadge(String pkg, int uid) { checkCallerIsSystem(); diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 4e3fbaa18870e..97133a56779d8 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -42,8 +42,10 @@ import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; +import android.content.pm.UserInfo; import android.metrics.LogMaker; import android.os.Binder; import android.os.Build; @@ -146,7 +148,6 @@ public class PreferencesHelper implements RankingConfig { static final boolean DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS = false; private static final boolean DEFAULT_SHOW_BADGE = true; - private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE = false; private static final boolean DEFAULT_APP_LOCKED_IMPORTANCE = false; static final boolean DEFAULT_BUBBLES_ENABLED = true; @@ -193,8 +194,6 @@ public class PreferencesHelper implements RankingConfig { private boolean mAllowInvalidShortcuts = false; - private Map> mOemLockedApps = new HashMap(); - public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, ZenModeHelper zenHelper, PermissionHelper permHelper, NotificationChannelLogger notificationChannelLogger, @@ -411,7 +410,7 @@ public class PreferencesHelper implements RankingConfig { channel.populateFromXml(parser); } channel.setImportanceLockedByCriticalDeviceFunction( - r.defaultAppLockedImportance); + r.defaultAppLockedImportance || r.fixedImportance); if (isShortcutOk(channel) && isDeletionOk(channel)) { r.channels.put(id, channel); @@ -484,14 +483,6 @@ public class PreferencesHelper implements RankingConfig { r.visibility = visibility; r.showBadge = showBadge; r.bubblePreference = bubblePreference; - if (mOemLockedApps.containsKey(r.pkg)) { - List channels = mOemLockedApps.get(r.pkg); - if (channels == null || channels.isEmpty()) { - r.oemLockedImportance = true; - } else { - r.oemLockedChannels = channels; - } - } try { createDefaultChannelIfNeededLocked(r); @@ -818,6 +809,13 @@ public class PreferencesHelper implements RankingConfig { } } + boolean isImportanceLocked(String pkg, int uid) { + synchronized (mPackagePreferences) { + PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); + return r.fixedImportance || r.defaultAppLockedImportance; + } + } + @Override public boolean isGroupBlocked(String packageName, int uid, String groupId) { if (groupId == null) { @@ -1008,7 +1006,7 @@ public class PreferencesHelper implements RankingConfig { clearLockedFieldsLocked(channel); channel.setImportanceLockedByCriticalDeviceFunction( - r.defaultAppLockedImportance); + r.defaultAppLockedImportance || r.fixedImportance); if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) { channel.setLockscreenVisibility( @@ -1090,8 +1088,7 @@ public class PreferencesHelper implements RankingConfig { updatedChannel.unlockFields(updatedChannel.getUserLockedFields()); } - if ((mPermissionHelper.isPermissionFixed(r.pkg, UserHandle.getUserId(r.uid)) - || channel.isImportanceLockedByCriticalDeviceFunction()) + if (channel.isImportanceLockedByCriticalDeviceFunction() && !(channel.isBlockable() || channel.getImportance() == IMPORTANCE_NONE)) { updatedChannel.setImportance(channel.getImportance()); } @@ -1267,6 +1264,28 @@ public class PreferencesHelper implements RankingConfig { mHideSilentStatusBarIcons = hide; } + public void updateFixedImportance(List users) { + for (UserInfo user : users) { + List packages = mPm.getInstalledPackagesAsUser( + PackageManager.PackageInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY), + user.getUserHandle().getIdentifier()); + for (PackageInfo pi : packages) { + boolean fixed = mPermissionHelper.isPermissionFixed( + pi.packageName, user.getUserHandle().getIdentifier()); + if (fixed) { + synchronized (mPackagePreferences) { + PackagePreferences p = getOrCreatePackagePreferencesLocked( + pi.packageName, pi.applicationInfo.uid); + p.fixedImportance = true; + for (NotificationChannel channel : p.channels.values()) { + channel.setImportanceLockedByCriticalDeviceFunction(true); + } + } + } + } + } + } + public void updateDefaultApps(int userId, ArraySet toRemove, ArraySet> toAdd) { synchronized (mPackagePreferences) { @@ -1274,8 +1293,10 @@ public class PreferencesHelper implements RankingConfig { if (userId == UserHandle.getUserId(p.uid)) { if (toRemove != null && toRemove.contains(p.pkg)) { p.defaultAppLockedImportance = false; - for (NotificationChannel channel : p.channels.values()) { - channel.setImportanceLockedByCriticalDeviceFunction(false); + if (!p.fixedImportance) { + for (NotificationChannel channel : p.channels.values()) { + channel.setImportanceLockedByCriticalDeviceFunction(false); + } } } } @@ -1934,13 +1955,9 @@ public class PreferencesHelper implements RankingConfig { pw.print(" defaultAppLocked="); pw.print(r.defaultAppLockedImportance); } - if (r.oemLockedImportance != DEFAULT_OEM_LOCKED_IMPORTANCE) { - pw.print(" oemLocked="); - pw.print(r.oemLockedImportance); - } - if (!r.oemLockedChannels.isEmpty()) { - pw.print(" futureLockedChannels="); - pw.print(r.oemLockedChannels); + if (r.fixedImportance != DEFAULT_APP_LOCKED_IMPORTANCE) { + pw.print(" fixedImportance="); + pw.print(r.fixedImportance); } pw.println(); for (NotificationChannel channel : r.channels.values()) { @@ -2682,9 +2699,8 @@ public class PreferencesHelper implements RankingConfig { int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS; // these fields are loaded on boot from a different source of truth and so are not // written to notification policy xml - boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE; - List oemLockedChannels = new ArrayList<>(); boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE; + boolean fixedImportance = DEFAULT_APP_LOCKED_IMPORTANCE; boolean hasSentInvalidMessage = false; boolean hasSentValidMessage = false; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index a5cec7e01e9aa..8d50ceaf74e98 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -85,6 +85,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; +import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Color; import android.media.AudioAttributes; @@ -121,6 +122,8 @@ import com.android.os.AtomsProto.PackageNotificationPreferences; import com.android.server.UiServiceTestCase; import com.android.server.notification.PermissionHelper.PackagePermission; +import com.google.common.collect.ImmutableList; + import org.json.JSONArray; import org.json.JSONObject; import org.junit.Before; @@ -3523,8 +3526,37 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testUpdateNotificationChannel_fixedPermission() { + List users = ImmutableList.of(new UserInfo(UserHandle.USER_SYSTEM, "user0", 0)); when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true); + PackageInfo pm = new PackageInfo(); + pm.packageName = PKG_O; + pm.applicationInfo = new ApplicationInfo(); + pm.applicationInfo.uid = UID_O; + List packages = ImmutableList.of(pm); + when(mPm.getInstalledPackagesAsUser(any(), anyInt())).thenReturn(packages); + mHelper.updateFixedImportance(users); + assertTrue(mHelper.isImportanceLocked(PKG_O, UID_O)); + + NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); + + NotificationChannel update = new NotificationChannel("a", "a", IMPORTANCE_NONE); + update.setAllowBubbles(false); + + mHelper.updateNotificationChannel(PKG_O, UID_O, update, true); + + assertEquals(IMPORTANCE_HIGH, + mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance()); + assertEquals(false, + mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble()); + } + + @Test + public void testUpdateNotificationChannel_defaultApp() { + ArraySet> toAdd = new ArraySet<>(); + toAdd.add(new Pair(PKG_O, UID_O)); + mHelper.updateDefaultApps(0, null, toAdd); NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); @@ -3594,6 +3626,58 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble()); } + @Test + public void testUpdateFixedImportance_multiUser() { + NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); + NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW); + NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT); + // different uids, same package + mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); + mHelper.createNotificationChannel(PKG_O, UID_O, b, false, false); + mHelper.createNotificationChannel(PKG_O, UserHandle.PER_USER_RANGE + 1, c, true, true); + + UserInfo user = new UserInfo(); + user.id = 0; + List users = ImmutableList.of(user); + when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true); + PackageInfo pm = new PackageInfo(); + pm.packageName = PKG_O; + pm.applicationInfo = new ApplicationInfo(); + pm.applicationInfo.uid = UID_O; + List packages = ImmutableList.of(pm); + when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages); + mHelper.updateFixedImportance(users); + + assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) + .isImportanceLockedByCriticalDeviceFunction()); + assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, b.getId(), false) + .isImportanceLockedByCriticalDeviceFunction()); + assertFalse(mHelper.getNotificationChannel( + PKG_O, UserHandle.PER_USER_RANGE + 1, c.getId(), false) + .isImportanceLockedByCriticalDeviceFunction()); + } + + @Test + public void testUpdateFixedImportance_channelDoesNotExistYet() { + UserInfo user = new UserInfo(); + user.id = 0; + List users = ImmutableList.of(user); + when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true); + PackageInfo pm = new PackageInfo(); + pm.packageName = PKG_O; + pm.applicationInfo = new ApplicationInfo(); + pm.applicationInfo.uid = UID_O; + List packages = ImmutableList.of(pm); + when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages); + mHelper.updateFixedImportance(users); + + NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); + + assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) + .isImportanceLockedByCriticalDeviceFunction()); + } + @Test public void testUpdateDefaultApps_add_multiUser() { NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); @@ -3758,6 +3842,62 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertTrue(a.isImportanceLockedByCriticalDeviceFunction()); } + @Test + public void testUpdateFixedImportance_thenDefaultAppsRemoves() { + UserInfo user = new UserInfo(); + user.id = 0; + List users = ImmutableList.of(user); + when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true); + PackageInfo pm = new PackageInfo(); + pm.packageName = PKG_O; + pm.applicationInfo = new ApplicationInfo(); + pm.applicationInfo.uid = UID_O; + List packages = ImmutableList.of(pm); + when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages); + mHelper.updateFixedImportance(users); + + ArraySet toRemove = new ArraySet<>(); + toRemove.add(PKG_O); + mHelper.updateDefaultApps(0, toRemove, null); + + assertTrue(mHelper.isImportanceLocked(PKG_O, UID_O)); + + NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); + + // Still locked by permission if not role + assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) + .isImportanceLockedByCriticalDeviceFunction()); + } + + @Test + public void testUpdateDefaultApps_thenNotFixedPermission() { + ArraySet> toAdd = new ArraySet<>(); + toAdd.add(new Pair(PKG_O, UID_O)); + mHelper.updateDefaultApps(0, null, toAdd); + + UserInfo user = new UserInfo(); + user.id = 0; + List users = ImmutableList.of(user); + when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(false); + PackageInfo pm = new PackageInfo(); + pm.packageName = PKG_O; + pm.applicationInfo = new ApplicationInfo(); + pm.applicationInfo.uid = UID_O; + List packages = ImmutableList.of(pm); + when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages); + mHelper.updateFixedImportance(users); + + assertTrue(mHelper.isImportanceLocked(PKG_O, UID_O)); + + NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); + mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); + + // Still locked by role if not permission + assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) + .isImportanceLockedByCriticalDeviceFunction()); + } + @Test public void testChannelXml_backupDefaultApp() throws Exception { NotificationChannel channel1 =