Loading core/java/android/app/INotificationManager.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -124,7 +124,7 @@ interface INotificationManager boolean onlyHasDefaultChannel(String pkg, int uid); boolean areChannelsBypassingDnd(); ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int uid); List<String> getPackagesBypassingDnd(int userId, boolean includeConversationChannels); ParceledListSlice getPackagesBypassingDnd(int userId); boolean isPackagePaused(String pkg); void deleteNotificationHistoryItem(String pkg, int uid, long postedTime); boolean isPermissionFixed(String pkg, int userId); Loading core/java/android/app/ZenBypassingApp.java 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.NonNull; import java.util.Objects; /** * @hide */ public final class ZenBypassingApp implements Parcelable { @NonNull private String mPkg; private boolean mAllChannelsBypass; public ZenBypassingApp(@NonNull String pkg, boolean allChannelsBypass) { mPkg = pkg; mAllChannelsBypass = allChannelsBypass; } public ZenBypassingApp(Parcel source) { mPkg = source.readString(); mAllChannelsBypass = source.readBoolean(); } @NonNull public String getPkg() { return mPkg; } public boolean doAllChannelsBypass() { return mAllChannelsBypass; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mPkg); dest.writeBoolean(mAllChannelsBypass); } public static final @android.annotation.NonNull Parcelable.Creator<ZenBypassingApp> CREATOR = new Parcelable.Creator<ZenBypassingApp>() { @Override public ZenBypassingApp createFromParcel(Parcel source) { return new ZenBypassingApp(source); } @Override public ZenBypassingApp[] newArray(int size) { return new ZenBypassingApp[size]; } }; @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ZenBypassingApp)) return false; ZenBypassingApp that = (ZenBypassingApp) o; return mAllChannelsBypass == that.mAllChannelsBypass && Objects.equals(mPkg, that.mPkg); } @Override public int hashCode() { return Objects.hash(mPkg, mAllChannelsBypass); } @Override public String toString() { return "ZenBypassingApp{" + "mPkg='" + mPkg + '\'' + ", mAllChannelsBypass=" + mAllChannelsBypass + '}'; } } services/core/java/com/android/server/notification/NotificationManagerService.java +16 −26 Original line number Diff line number Diff line Loading @@ -211,6 +211,7 @@ import android.app.RemoteServiceException.BadForegroundServiceNotificationExcept import android.app.RemoteServiceException.BadUserInitiatedJobNotificationException; import android.app.StatsManager; import android.app.UriGrantsManager; import android.app.ZenBypassingApp; import android.app.admin.DevicePolicyManagerInternal; import android.app.backup.BackupManager; import android.app.backup.BackupRestoreEventLogger; Loading Loading @@ -238,7 +239,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; import android.content.pm.ModuleInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; Loading Loading @@ -4043,7 +4043,7 @@ public class NotificationManagerService extends SystemService { "canNotifyAsPackage for uid " + uid); } return areNotificationsEnabledForPackageInt(pkg, uid); return areNotificationsEnabledForPackageInt(uid); } /** Loading Loading @@ -4864,30 +4864,20 @@ public class NotificationManagerService extends SystemService { } @Override public List<String> getPackagesBypassingDnd(int userId, boolean includeConversationChannels) { public ParceledListSlice<ZenBypassingApp> getPackagesBypassingDnd(int userId) throws RemoteException { checkCallerIsSystem(); final ArraySet<String> packageNames = new ArraySet<>(); List<PackageInfo> pkgs = mPackageManagerClient.getInstalledPackagesAsUser(0, userId); for (PackageInfo pi : pkgs) { String pkg = pi.packageName; // If any NotificationChannel for this package is bypassing, the // package is considered bypassing. for (NotificationChannel channel : getNotificationChannelsBypassingDnd(pkg, pi.applicationInfo.uid).getList()) { // Skips non-demoted conversation channels. if (!includeConversationChannels && !TextUtils.isEmpty(channel.getConversationId()) && !channel.isDemoted()) { continue; UserHandle user = UserHandle.of(userId); ArrayList<ZenBypassingApp> bypassing = mPreferencesHelper.getPackagesBypassingDnd(userId); for (int i = bypassing.size() - 1; i >= 0; i--) { String pkg = bypassing.get(i).getPkg(); if (!areNotificationsEnabledForPackage(pkg, getUidForPackageAndUser(pkg, user))) { bypassing.remove(i); } packageNames.add(pkg); } } return new ArrayList<String>(packageNames); return new ParceledListSlice<>(bypassing); } @Override Loading Loading @@ -7763,7 +7753,7 @@ public class NotificationManagerService extends SystemService { @Override public boolean areNotificationsEnabledForPackage(String pkg, int uid) { return areNotificationsEnabledForPackageInt(pkg, uid); return areNotificationsEnabledForPackageInt(uid); } @Override Loading Loading @@ -8742,7 +8732,7 @@ public class NotificationManagerService extends SystemService { } // blocked apps boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid); boolean isBlocked = !areNotificationsEnabledForPackageInt(uid); synchronized (mNotificationLock) { isBlocked |= isRecordBlockedLocked(r); } Loading Loading @@ -8792,7 +8782,7 @@ public class NotificationManagerService extends SystemService { } } private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) { private boolean areNotificationsEnabledForPackageInt(int uid) { return mPermissionHelper.hasPermission(uid); } Loading Loading @@ -9328,7 +9318,7 @@ public class NotificationManagerService extends SystemService { * notifying all listeners to a background thread; false otherwise. */ private boolean postNotification() { boolean appBanned = !areNotificationsEnabledForPackageInt(pkg, uid); boolean appBanned = !areNotificationsEnabledForPackageInt(uid); boolean isCallNotification = isCallNotification(pkg, uid); boolean posted = false; synchronized (NotificationManagerService.this.mNotificationLock) { Loading services/core/java/com/android/server/notification/PreferencesHelper.java +30 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.app.ZenBypassingApp; import android.content.AttributionSource; import android.content.Context; import android.content.pm.ApplicationInfo; Loading Loading @@ -1949,6 +1950,35 @@ public class PreferencesHelper implements RankingConfig { return new ParceledListSlice<>(channels); } /** * Gets all apps that can bypass DND, and a boolean indicating whether all (true) or some * (false) of its notification channels can currently bypass. */ public @NonNull ArrayList<ZenBypassingApp> getPackagesBypassingDnd(@UserIdInt int userId) { ArrayList<ZenBypassingApp> bypassing = new ArrayList<>(); synchronized (mLock) { for (PackagePreferences p : mPackagePreferences.values()) { if (p.userId != userId) { continue; } int totalChannelCount = p.channels.size(); int bypassingCount = 0; if (totalChannelCount == 0) { continue; } for (NotificationChannel channel : p.channels.values()) { if (channelIsLiveLocked(p, channel) && channel.canBypassDnd()) { bypassingCount++; } } if (bypassingCount > 0) { bypassing.add(new ZenBypassingApp(p.pkg, totalChannelCount == bypassingCount)); } } } return bypassing; } /** * True for pre-O apps that only have the default channel, or pre O apps that have no * channels yet. This method will create the default channel for pre-O apps that don't have it. Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +33 −115 Original line number Diff line number Diff line Loading @@ -210,6 +210,7 @@ import android.app.Person; import android.app.RemoteInput; import android.app.RemoteInputHistoryItem; import android.app.StatsManager; import android.app.ZenBypassingApp; import android.app.admin.DevicePolicyManagerInternal; import android.app.backup.BackupRestoreEventLogger; import android.app.job.JobScheduler; Loading Loading @@ -13173,6 +13174,37 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("test", 1002))); } @Test public void getPackagesBypassingDnd_blocked() throws RemoteException, PackageManager.NameNotFoundException { NotificationChannel channel1 = new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MAX); NotificationChannel channel2 = new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_MAX); NotificationChannel channel3 = new NotificationChannel("id4", "name3", NotificationManager.IMPORTANCE_MAX); channel1.setBypassDnd(true); channel2.setBypassDnd(true); channel3.setBypassDnd(false); // has DND access, so can set bypassDnd attribute mService.mPreferencesHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, /*has DND access*/ true, UID_N_MR1, false); mService.mPreferencesHelper.createNotificationChannel(PKG_P, UID_P, channel2, true, true, UID_P, false); mService.mPreferencesHelper.createNotificationChannel(PKG_P, UID_P, channel3, true, true, UID_P, false); when(mPackageManager.getPackageUid(eq(PKG_P), anyLong(), anyInt())).thenReturn(UID_P); when(mPackageManager.getPackageUid(eq(PKG_N_MR1), anyLong(), anyInt())) .thenReturn(UID_N_MR1); when(mPermissionHelper.hasPermission(UID_N_MR1)).thenReturn(false); when(mPermissionHelper.hasPermission(UID_P)).thenReturn(true); assertThat(mBinderService.getPackagesBypassingDnd(UserHandle.getUserId(UID_P)).getList()) .containsExactly(new ZenBypassingApp(PKG_P, false)); } @Test public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException { mService.setPreferencesHelper(mPreferencesHelper); Loading @@ -13187,124 +13219,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testGetPackagesBypassingDnd_empty() throws RemoteException { mService.setPreferencesHelper(mPreferencesHelper); List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, true); List<String> result = mBinderService.getPackagesBypassingDnd(mUserId).getList(); assertThat(result).isEmpty(); } @Test public void testGetPackagesBypassingDnd_excludeConversationChannels() throws RemoteException { mService.setPreferencesHelper(mPreferencesHelper); // Set packages PackageInfo pkg0 = new PackageInfo(); pkg0.packageName = "pkg0"; pkg0.applicationInfo = new ApplicationInfo(); pkg0.applicationInfo.uid = mUid; PackageInfo pkg1 = new PackageInfo(); pkg1.packageName = "pkg1"; pkg1.applicationInfo = new ApplicationInfo(); pkg1.applicationInfo.uid = mUid; PackageInfo pkg2 = new PackageInfo(); pkg2.packageName = "pkg2"; pkg2.applicationInfo = new ApplicationInfo(); pkg2.applicationInfo.uid = mUid; when(mPackageManagerClient.getInstalledPackagesAsUser(0, mUserId)) .thenReturn(List.of(pkg0, pkg1, pkg2)); // Conversation channels NotificationChannel nc0 = new NotificationChannel("id0", "id0", NotificationManager.IMPORTANCE_HIGH); nc0.setConversationId("parentChannel", "conversationId"); // Demoted conversation channel NotificationChannel nc1 = new NotificationChannel("id1", "id1", NotificationManager.IMPORTANCE_HIGH); nc1.setConversationId("parentChannel", "conversationId"); nc1.setDemoted(true); // Non-conversation channels NotificationChannel nc2 = new NotificationChannel("id2", "id2", NotificationManager.IMPORTANCE_HIGH); NotificationChannel nc3 = new NotificationChannel("id3", "id3", NotificationManager.IMPORTANCE_HIGH); ParceledListSlice<NotificationChannel> pls0 = new ParceledListSlice(ImmutableList.of(nc0)); ParceledListSlice<NotificationChannel> pls1 = new ParceledListSlice(ImmutableList.of(nc1)); ParceledListSlice<NotificationChannel> pls2 = new ParceledListSlice(ImmutableList.of(nc2, nc3)); when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg0", mUid)) .thenReturn(pls0); when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg1", mUid)) .thenReturn(pls1); when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg2", mUid)) .thenReturn(pls2); List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, false); assertThat(result).containsExactly("pkg1", "pkg2"); } @Test public void testGetPackagesBypassingDnd_includeConversationChannels() throws RemoteException { mService.setPreferencesHelper(mPreferencesHelper); // Set packages PackageInfo pkg0 = new PackageInfo(); pkg0.packageName = "pkg0"; pkg0.applicationInfo = new ApplicationInfo(); pkg0.applicationInfo.uid = mUid; PackageInfo pkg1 = new PackageInfo(); pkg1.packageName = "pkg1"; pkg1.applicationInfo = new ApplicationInfo(); pkg1.applicationInfo.uid = mUid; PackageInfo pkg2 = new PackageInfo(); pkg2.packageName = "pkg2"; pkg2.applicationInfo = new ApplicationInfo(); pkg2.applicationInfo.uid = mUid; when(mPackageManagerClient.getInstalledPackagesAsUser(0, mUserId)) .thenReturn(List.of(pkg0, pkg1, pkg2)); // Conversation channels NotificationChannel nc0 = new NotificationChannel("id0", "id0", NotificationManager.IMPORTANCE_HIGH); nc0.setConversationId("parentChannel", "conversationId"); // Demoted conversation channel NotificationChannel nc1 = new NotificationChannel("id1", "id1", NotificationManager.IMPORTANCE_HIGH); nc1.setConversationId("parentChannel", "conversationId"); nc1.setDemoted(true); // Non-conversation channels NotificationChannel nc2 = new NotificationChannel("id2", "id2", NotificationManager.IMPORTANCE_HIGH); NotificationChannel nc3 = new NotificationChannel("id3", "id3", NotificationManager.IMPORTANCE_HIGH); ParceledListSlice<NotificationChannel> pls0 = new ParceledListSlice(ImmutableList.of(nc0)); ParceledListSlice<NotificationChannel> pls1 = new ParceledListSlice(ImmutableList.of(nc1)); ParceledListSlice<NotificationChannel> pls2 = new ParceledListSlice(ImmutableList.of(nc2, nc3)); when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg0", mUid)) .thenReturn(pls0); when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg1", mUid)) .thenReturn(pls1); when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg2", mUid)) .thenReturn(pls2); List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, true); assertThat(result).containsExactly("pkg0", "pkg1", "pkg2"); } @Test public void testMatchesCallFilter_noPermissionShouldThrow() throws Exception { // set the testable NMS to not system uid/appid Loading
core/java/android/app/INotificationManager.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -124,7 +124,7 @@ interface INotificationManager boolean onlyHasDefaultChannel(String pkg, int uid); boolean areChannelsBypassingDnd(); ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int uid); List<String> getPackagesBypassingDnd(int userId, boolean includeConversationChannels); ParceledListSlice getPackagesBypassingDnd(int userId); boolean isPackagePaused(String pkg); void deleteNotificationHistoryItem(String pkg, int uid, long postedTime); boolean isPermissionFixed(String pkg, int userId); Loading
core/java/android/app/ZenBypassingApp.java 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.NonNull; import java.util.Objects; /** * @hide */ public final class ZenBypassingApp implements Parcelable { @NonNull private String mPkg; private boolean mAllChannelsBypass; public ZenBypassingApp(@NonNull String pkg, boolean allChannelsBypass) { mPkg = pkg; mAllChannelsBypass = allChannelsBypass; } public ZenBypassingApp(Parcel source) { mPkg = source.readString(); mAllChannelsBypass = source.readBoolean(); } @NonNull public String getPkg() { return mPkg; } public boolean doAllChannelsBypass() { return mAllChannelsBypass; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mPkg); dest.writeBoolean(mAllChannelsBypass); } public static final @android.annotation.NonNull Parcelable.Creator<ZenBypassingApp> CREATOR = new Parcelable.Creator<ZenBypassingApp>() { @Override public ZenBypassingApp createFromParcel(Parcel source) { return new ZenBypassingApp(source); } @Override public ZenBypassingApp[] newArray(int size) { return new ZenBypassingApp[size]; } }; @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ZenBypassingApp)) return false; ZenBypassingApp that = (ZenBypassingApp) o; return mAllChannelsBypass == that.mAllChannelsBypass && Objects.equals(mPkg, that.mPkg); } @Override public int hashCode() { return Objects.hash(mPkg, mAllChannelsBypass); } @Override public String toString() { return "ZenBypassingApp{" + "mPkg='" + mPkg + '\'' + ", mAllChannelsBypass=" + mAllChannelsBypass + '}'; } }
services/core/java/com/android/server/notification/NotificationManagerService.java +16 −26 Original line number Diff line number Diff line Loading @@ -211,6 +211,7 @@ import android.app.RemoteServiceException.BadForegroundServiceNotificationExcept import android.app.RemoteServiceException.BadUserInitiatedJobNotificationException; import android.app.StatsManager; import android.app.UriGrantsManager; import android.app.ZenBypassingApp; import android.app.admin.DevicePolicyManagerInternal; import android.app.backup.BackupManager; import android.app.backup.BackupRestoreEventLogger; Loading Loading @@ -238,7 +239,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; import android.content.pm.ModuleInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; Loading Loading @@ -4043,7 +4043,7 @@ public class NotificationManagerService extends SystemService { "canNotifyAsPackage for uid " + uid); } return areNotificationsEnabledForPackageInt(pkg, uid); return areNotificationsEnabledForPackageInt(uid); } /** Loading Loading @@ -4864,30 +4864,20 @@ public class NotificationManagerService extends SystemService { } @Override public List<String> getPackagesBypassingDnd(int userId, boolean includeConversationChannels) { public ParceledListSlice<ZenBypassingApp> getPackagesBypassingDnd(int userId) throws RemoteException { checkCallerIsSystem(); final ArraySet<String> packageNames = new ArraySet<>(); List<PackageInfo> pkgs = mPackageManagerClient.getInstalledPackagesAsUser(0, userId); for (PackageInfo pi : pkgs) { String pkg = pi.packageName; // If any NotificationChannel for this package is bypassing, the // package is considered bypassing. for (NotificationChannel channel : getNotificationChannelsBypassingDnd(pkg, pi.applicationInfo.uid).getList()) { // Skips non-demoted conversation channels. if (!includeConversationChannels && !TextUtils.isEmpty(channel.getConversationId()) && !channel.isDemoted()) { continue; UserHandle user = UserHandle.of(userId); ArrayList<ZenBypassingApp> bypassing = mPreferencesHelper.getPackagesBypassingDnd(userId); for (int i = bypassing.size() - 1; i >= 0; i--) { String pkg = bypassing.get(i).getPkg(); if (!areNotificationsEnabledForPackage(pkg, getUidForPackageAndUser(pkg, user))) { bypassing.remove(i); } packageNames.add(pkg); } } return new ArrayList<String>(packageNames); return new ParceledListSlice<>(bypassing); } @Override Loading Loading @@ -7763,7 +7753,7 @@ public class NotificationManagerService extends SystemService { @Override public boolean areNotificationsEnabledForPackage(String pkg, int uid) { return areNotificationsEnabledForPackageInt(pkg, uid); return areNotificationsEnabledForPackageInt(uid); } @Override Loading Loading @@ -8742,7 +8732,7 @@ public class NotificationManagerService extends SystemService { } // blocked apps boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid); boolean isBlocked = !areNotificationsEnabledForPackageInt(uid); synchronized (mNotificationLock) { isBlocked |= isRecordBlockedLocked(r); } Loading Loading @@ -8792,7 +8782,7 @@ public class NotificationManagerService extends SystemService { } } private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) { private boolean areNotificationsEnabledForPackageInt(int uid) { return mPermissionHelper.hasPermission(uid); } Loading Loading @@ -9328,7 +9318,7 @@ public class NotificationManagerService extends SystemService { * notifying all listeners to a background thread; false otherwise. */ private boolean postNotification() { boolean appBanned = !areNotificationsEnabledForPackageInt(pkg, uid); boolean appBanned = !areNotificationsEnabledForPackageInt(uid); boolean isCallNotification = isCallNotification(pkg, uid); boolean posted = false; synchronized (NotificationManagerService.this.mNotificationLock) { Loading
services/core/java/com/android/server/notification/PreferencesHelper.java +30 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.app.ZenBypassingApp; import android.content.AttributionSource; import android.content.Context; import android.content.pm.ApplicationInfo; Loading Loading @@ -1949,6 +1950,35 @@ public class PreferencesHelper implements RankingConfig { return new ParceledListSlice<>(channels); } /** * Gets all apps that can bypass DND, and a boolean indicating whether all (true) or some * (false) of its notification channels can currently bypass. */ public @NonNull ArrayList<ZenBypassingApp> getPackagesBypassingDnd(@UserIdInt int userId) { ArrayList<ZenBypassingApp> bypassing = new ArrayList<>(); synchronized (mLock) { for (PackagePreferences p : mPackagePreferences.values()) { if (p.userId != userId) { continue; } int totalChannelCount = p.channels.size(); int bypassingCount = 0; if (totalChannelCount == 0) { continue; } for (NotificationChannel channel : p.channels.values()) { if (channelIsLiveLocked(p, channel) && channel.canBypassDnd()) { bypassingCount++; } } if (bypassingCount > 0) { bypassing.add(new ZenBypassingApp(p.pkg, totalChannelCount == bypassingCount)); } } } return bypassing; } /** * True for pre-O apps that only have the default channel, or pre O apps that have no * channels yet. This method will create the default channel for pre-O apps that don't have it. Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +33 −115 Original line number Diff line number Diff line Loading @@ -210,6 +210,7 @@ import android.app.Person; import android.app.RemoteInput; import android.app.RemoteInputHistoryItem; import android.app.StatsManager; import android.app.ZenBypassingApp; import android.app.admin.DevicePolicyManagerInternal; import android.app.backup.BackupRestoreEventLogger; import android.app.job.JobScheduler; Loading Loading @@ -13173,6 +13174,37 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("test", 1002))); } @Test public void getPackagesBypassingDnd_blocked() throws RemoteException, PackageManager.NameNotFoundException { NotificationChannel channel1 = new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MAX); NotificationChannel channel2 = new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_MAX); NotificationChannel channel3 = new NotificationChannel("id4", "name3", NotificationManager.IMPORTANCE_MAX); channel1.setBypassDnd(true); channel2.setBypassDnd(true); channel3.setBypassDnd(false); // has DND access, so can set bypassDnd attribute mService.mPreferencesHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, /*has DND access*/ true, UID_N_MR1, false); mService.mPreferencesHelper.createNotificationChannel(PKG_P, UID_P, channel2, true, true, UID_P, false); mService.mPreferencesHelper.createNotificationChannel(PKG_P, UID_P, channel3, true, true, UID_P, false); when(mPackageManager.getPackageUid(eq(PKG_P), anyLong(), anyInt())).thenReturn(UID_P); when(mPackageManager.getPackageUid(eq(PKG_N_MR1), anyLong(), anyInt())) .thenReturn(UID_N_MR1); when(mPermissionHelper.hasPermission(UID_N_MR1)).thenReturn(false); when(mPermissionHelper.hasPermission(UID_P)).thenReturn(true); assertThat(mBinderService.getPackagesBypassingDnd(UserHandle.getUserId(UID_P)).getList()) .containsExactly(new ZenBypassingApp(PKG_P, false)); } @Test public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException { mService.setPreferencesHelper(mPreferencesHelper); Loading @@ -13187,124 +13219,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testGetPackagesBypassingDnd_empty() throws RemoteException { mService.setPreferencesHelper(mPreferencesHelper); List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, true); List<String> result = mBinderService.getPackagesBypassingDnd(mUserId).getList(); assertThat(result).isEmpty(); } @Test public void testGetPackagesBypassingDnd_excludeConversationChannels() throws RemoteException { mService.setPreferencesHelper(mPreferencesHelper); // Set packages PackageInfo pkg0 = new PackageInfo(); pkg0.packageName = "pkg0"; pkg0.applicationInfo = new ApplicationInfo(); pkg0.applicationInfo.uid = mUid; PackageInfo pkg1 = new PackageInfo(); pkg1.packageName = "pkg1"; pkg1.applicationInfo = new ApplicationInfo(); pkg1.applicationInfo.uid = mUid; PackageInfo pkg2 = new PackageInfo(); pkg2.packageName = "pkg2"; pkg2.applicationInfo = new ApplicationInfo(); pkg2.applicationInfo.uid = mUid; when(mPackageManagerClient.getInstalledPackagesAsUser(0, mUserId)) .thenReturn(List.of(pkg0, pkg1, pkg2)); // Conversation channels NotificationChannel nc0 = new NotificationChannel("id0", "id0", NotificationManager.IMPORTANCE_HIGH); nc0.setConversationId("parentChannel", "conversationId"); // Demoted conversation channel NotificationChannel nc1 = new NotificationChannel("id1", "id1", NotificationManager.IMPORTANCE_HIGH); nc1.setConversationId("parentChannel", "conversationId"); nc1.setDemoted(true); // Non-conversation channels NotificationChannel nc2 = new NotificationChannel("id2", "id2", NotificationManager.IMPORTANCE_HIGH); NotificationChannel nc3 = new NotificationChannel("id3", "id3", NotificationManager.IMPORTANCE_HIGH); ParceledListSlice<NotificationChannel> pls0 = new ParceledListSlice(ImmutableList.of(nc0)); ParceledListSlice<NotificationChannel> pls1 = new ParceledListSlice(ImmutableList.of(nc1)); ParceledListSlice<NotificationChannel> pls2 = new ParceledListSlice(ImmutableList.of(nc2, nc3)); when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg0", mUid)) .thenReturn(pls0); when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg1", mUid)) .thenReturn(pls1); when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg2", mUid)) .thenReturn(pls2); List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, false); assertThat(result).containsExactly("pkg1", "pkg2"); } @Test public void testGetPackagesBypassingDnd_includeConversationChannels() throws RemoteException { mService.setPreferencesHelper(mPreferencesHelper); // Set packages PackageInfo pkg0 = new PackageInfo(); pkg0.packageName = "pkg0"; pkg0.applicationInfo = new ApplicationInfo(); pkg0.applicationInfo.uid = mUid; PackageInfo pkg1 = new PackageInfo(); pkg1.packageName = "pkg1"; pkg1.applicationInfo = new ApplicationInfo(); pkg1.applicationInfo.uid = mUid; PackageInfo pkg2 = new PackageInfo(); pkg2.packageName = "pkg2"; pkg2.applicationInfo = new ApplicationInfo(); pkg2.applicationInfo.uid = mUid; when(mPackageManagerClient.getInstalledPackagesAsUser(0, mUserId)) .thenReturn(List.of(pkg0, pkg1, pkg2)); // Conversation channels NotificationChannel nc0 = new NotificationChannel("id0", "id0", NotificationManager.IMPORTANCE_HIGH); nc0.setConversationId("parentChannel", "conversationId"); // Demoted conversation channel NotificationChannel nc1 = new NotificationChannel("id1", "id1", NotificationManager.IMPORTANCE_HIGH); nc1.setConversationId("parentChannel", "conversationId"); nc1.setDemoted(true); // Non-conversation channels NotificationChannel nc2 = new NotificationChannel("id2", "id2", NotificationManager.IMPORTANCE_HIGH); NotificationChannel nc3 = new NotificationChannel("id3", "id3", NotificationManager.IMPORTANCE_HIGH); ParceledListSlice<NotificationChannel> pls0 = new ParceledListSlice(ImmutableList.of(nc0)); ParceledListSlice<NotificationChannel> pls1 = new ParceledListSlice(ImmutableList.of(nc1)); ParceledListSlice<NotificationChannel> pls2 = new ParceledListSlice(ImmutableList.of(nc2, nc3)); when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg0", mUid)) .thenReturn(pls0); when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg1", mUid)) .thenReturn(pls1); when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg2", mUid)) .thenReturn(pls2); List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, true); assertThat(result).containsExactly("pkg0", "pkg1", "pkg2"); } @Test public void testMatchesCallFilter_noPermissionShouldThrow() throws Exception { // set the testable NMS to not system uid/appid