Merge "Update ZenModeAddBypassingApps to not binder call for every app" into main
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 3ce377e..31972ed 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -23,6 +23,7 @@
import static com.android.server.notification.Flags.notificationHideUnusedChannels;
+import android.annotation.FlaggedApi;
import android.app.Flags;
import android.app.INotificationManager;
import android.app.NotificationChannel;
@@ -67,10 +68,13 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
public class NotificationBackend {
private static final String TAG = "NotificationBackend";
@@ -368,6 +372,20 @@
}
}
+ /**
+ * Returns a set of all apps that have any notification channels (not including deleted ones).
+ */
+ @FlaggedApi(Flags.FLAG_NM_BINDER_PERF_GET_APPS_WITH_CHANNELS)
+ public @NonNull Set<String> getPackagesWithAnyChannels(int userId) {
+ try {
+ List<String> packages = sINM.getPackagesWithAnyChannels(userId);
+ return new HashSet<>(packages);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return Collections.EMPTY_SET;
+ }
+ }
+
public void updateChannel(String pkg, int uid, NotificationChannel channel) {
try {
sINM.updateNotificationChannelForPackage(pkg, uid, channel);
diff --git a/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceController.java
index ccd35ec..37f2205 100644
--- a/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceController.java
@@ -17,11 +17,13 @@
package com.android.settings.notification.modes;
import android.app.Application;
+import android.app.Flags;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
+import android.os.UserManager;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -44,7 +46,11 @@
import com.android.settingslib.widget.AppPreference;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
@@ -58,6 +64,8 @@
private static final String KEY = "zen_mode_non_bypassing_apps_list";
private static final String KEY_ADD = "zen_mode_bypassing_apps_add";
@Nullable private final NotificationBackend mNotificationBackend;
+ @Nullable private final ZenHelperBackend mHelperBackend;
+ @Nullable private final UserManager mUserManager;
@Nullable @VisibleForTesting ApplicationsState mApplicationsState;
@VisibleForTesting PreferenceScreen mPreferenceScreen;
@@ -69,18 +77,22 @@
@Nullable private Fragment mHostFragment;
public ZenModeAddBypassingAppsPreferenceController(Context context, @Nullable Application app,
- @Nullable Fragment host, @Nullable NotificationBackend notificationBackend) {
+ @Nullable Fragment host, @Nullable NotificationBackend notificationBackend,
+ @Nullable ZenHelperBackend helperBackend) {
this(context, app == null ? null : ApplicationsState.getInstance(app), host,
- notificationBackend);
+ notificationBackend, helperBackend);
}
private ZenModeAddBypassingAppsPreferenceController(Context context,
@Nullable ApplicationsState appState, @Nullable Fragment host,
- @Nullable NotificationBackend notificationBackend) {
+ @Nullable NotificationBackend notificationBackend,
+ @Nullable ZenHelperBackend helperBackend) {
super(context);
mNotificationBackend = notificationBackend;
mApplicationsState = appState;
mHostFragment = host;
+ mHelperBackend = helperBackend;
+ mUserManager = context.getSystemService(UserManager.class);
}
@Override
@@ -147,7 +159,7 @@
@VisibleForTesting
void updateAppList(List<ApplicationsState.AppEntry> apps) {
- if (apps == null) {
+ if (apps == null || mNotificationBackend == null) {
return;
}
@@ -157,21 +169,49 @@
mPreferenceScreen.addPreference(mPreferenceCategory);
}
+ Map<Integer, Set<String>> packagesByUser = new HashMap<>();
+ Map<Integer, Map<String, Boolean>> packagesBypassingDndByUser = new HashMap<>();
+ if (Flags.nmBinderPerfGetAppsWithChannels()) {
+ if (mHelperBackend == null || mUserManager == null) {
+ return;
+ }
+ for (UserHandle userHandle : mUserManager.getUserProfiles()) {
+ int userId = userHandle.getIdentifier();
+ packagesByUser.put(userId, mNotificationBackend.getPackagesWithAnyChannels(userId));
+ packagesBypassingDndByUser.put(userId,
+ mHelperBackend.getPackagesBypassingDnd(userId));
+ }
+ }
boolean doAnyAppsPassCriteria = false;
for (ApplicationsState.AppEntry app : apps) {
String pkg = app.info.packageName;
final String key = getKey(pkg, app.info.uid);
- final int appChannels = mNotificationBackend.getChannelCount(pkg, app.info.uid);
- final int appChannelsBypassingDnd = mNotificationBackend
- .getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size();
- if (appChannelsBypassingDnd == 0 && appChannels > 0) {
+ int userId = UserHandle.getUserId(app.info.uid);
+
+ boolean doesAppBypassDnd, doesAppHaveAnyChannels;
+ if (Flags.nmBinderPerfGetAppsWithChannels()) {
+ Set<String> packagesWithChannels = packagesByUser.getOrDefault(userId,
+ Collections.EMPTY_SET);
+ Map<String, Boolean> packagesBypassingDnd =
+ packagesBypassingDndByUser.getOrDefault(userId, new HashMap<>());
+ doesAppBypassDnd = packagesBypassingDnd.containsKey(pkg);
+ doesAppHaveAnyChannels = packagesWithChannels.contains(pkg);
+ } else {
+ final int appChannels = mNotificationBackend.getChannelCount(pkg, app.info.uid);
+ final int appChannelsBypassingDnd = mNotificationBackend
+ .getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size();
+ doesAppBypassDnd = (appChannelsBypassingDnd != 0);
+ doesAppHaveAnyChannels = (appChannels > 0);
+ }
+
+ if (!doesAppBypassDnd && doesAppHaveAnyChannels) {
doAnyAppsPassCriteria = true;
}
Preference pref = mPreferenceCategory.findPreference(key);
if (pref == null) {
- if (appChannelsBypassingDnd == 0 && appChannels > 0) {
+ if (!doesAppBypassDnd && doesAppHaveAnyChannels) {
// does not exist but should
pref = new AppPreference(mPrefContext);
pref.setKey(key);
@@ -193,7 +233,7 @@
updateIcon(pref, app);
mPreferenceCategory.addPreference(pref);
}
- } else if (appChannelsBypassingDnd != 0 || appChannels == 0) {
+ } else if (doesAppBypassDnd || !doesAppHaveAnyChannels) {
// exists but shouldn't anymore
mPreferenceCategory.removePreference(pref);
}
diff --git a/src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java b/src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java
index 21f34a2..05703fb 100644
--- a/src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModeSelectBypassingAppsFragment.java
@@ -60,7 +60,7 @@
controllers.add(new ZenModeAllBypassingAppsPreferenceController(context, app, host,
zenHelperBackend));
controllers.add(new ZenModeAddBypassingAppsPreferenceController(context, app, host,
- notificationBackend));
+ notificationBackend, zenHelperBackend));
return controllers;
}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceControllerTest.java
index c524ab9..e104939 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAddBypassingAppsPreferenceControllerTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -30,6 +31,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ParceledListSlice;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.UsesFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.fragment.app.Fragment;
@@ -46,36 +49,50 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
+import org.robolectric.ParameterizedRobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
-@RunWith(RobolectricTestRunner.class)
+@RunWith(ParameterizedRobolectricTestRunner.class)
+@UsesFlags(android.app.Flags.class)
@EnableFlags(Flags.FLAG_MODES_UI)
public class ZenModeAddBypassingAppsPreferenceControllerTest {
-
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock
private NotificationBackend mBackend;
@Mock
+ private ZenHelperBackend mHelperBackend;
+ @Mock
private PreferenceCategory mPreferenceCategory;
@Mock
private ApplicationsState mApplicationState;
private ZenModeAddBypassingAppsPreferenceController mController;
private Context mContext;
+ @ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(
+ Flags.FLAG_NM_BINDER_PERF_GET_APPS_WITH_CHANNELS);
+ }
+
+ public ZenModeAddBypassingAppsPreferenceControllerTest(FlagsParameterization flags) {
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new ZenModeAddBypassingAppsPreferenceController(
- mContext, null, mock(Fragment.class), mBackend);
+ mContext, null, mock(Fragment.class), mBackend, mHelperBackend);
mController.mPreferenceCategory = mPreferenceCategory;
mController.mApplicationsState = mApplicationState;
mController.mPrefContext = mContext;
@@ -132,6 +149,12 @@
appWithChannelsNoneBypassing.info.uid))
.thenReturn(new ParceledListSlice<>(new ArrayList<>()));
+ // used when NM_BINDER_PERF_GET_APPS_WITH_CHANNELS flag is true
+ when(mBackend.getPackagesWithAnyChannels(anyInt())).thenReturn(
+ Set.of("appWithBypassingChannels", "appWithChannelsNoneBypassing"));
+ when(mHelperBackend.getPackagesBypassingDnd(anyInt())).thenReturn(
+ Map.of("appWithBypassingChannels", false));
+
List<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
appEntries.add(appWithBypassingChannels);
appEntries.add(appWithoutChannels);