Merge "Update ZenModeAddBypassingApps to not binder call for every app" into main

This commit is contained in:
Yuri Lin
2025-02-12 08:05:39 -08:00
committed by Android (Google) Code Review
4 changed files with 96 additions and 15 deletions

View File

@@ -23,6 +23,7 @@ import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY
import static com.android.server.notification.Flags.notificationHideUnusedChannels; import static com.android.server.notification.Flags.notificationHideUnusedChannels;
import android.annotation.FlaggedApi;
import android.app.Flags; import android.app.Flags;
import android.app.INotificationManager; import android.app.INotificationManager;
import android.app.NotificationChannel; import android.app.NotificationChannel;
@@ -67,10 +68,13 @@ import com.android.settingslib.utils.StringUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
public class NotificationBackend { public class NotificationBackend {
private static final String TAG = "NotificationBackend"; private static final String TAG = "NotificationBackend";
@@ -368,6 +372,20 @@ public class NotificationBackend {
} }
} }
/**
* 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) { public void updateChannel(String pkg, int uid, NotificationChannel channel) {
try { try {
sINM.updateNotificationChannelForPackage(pkg, uid, channel); sINM.updateNotificationChannelForPackage(pkg, uid, channel);

View File

@@ -17,11 +17,13 @@
package com.android.settings.notification.modes; package com.android.settings.notification.modes;
import android.app.Application; import android.app.Application;
import android.app.Flags;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
@@ -44,7 +46,11 @@ import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.AppPreference; import com.android.settingslib.widget.AppPreference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
@@ -58,6 +64,8 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere
private static final String KEY = "zen_mode_non_bypassing_apps_list"; private static final String KEY = "zen_mode_non_bypassing_apps_list";
private static final String KEY_ADD = "zen_mode_bypassing_apps_add"; private static final String KEY_ADD = "zen_mode_bypassing_apps_add";
@Nullable private final NotificationBackend mNotificationBackend; @Nullable private final NotificationBackend mNotificationBackend;
@Nullable private final ZenHelperBackend mHelperBackend;
@Nullable private final UserManager mUserManager;
@Nullable @VisibleForTesting ApplicationsState mApplicationsState; @Nullable @VisibleForTesting ApplicationsState mApplicationsState;
@VisibleForTesting PreferenceScreen mPreferenceScreen; @VisibleForTesting PreferenceScreen mPreferenceScreen;
@@ -69,18 +77,22 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere
@Nullable private Fragment mHostFragment; @Nullable private Fragment mHostFragment;
public ZenModeAddBypassingAppsPreferenceController(Context context, @Nullable Application app, 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, this(context, app == null ? null : ApplicationsState.getInstance(app), host,
notificationBackend); notificationBackend, helperBackend);
} }
private ZenModeAddBypassingAppsPreferenceController(Context context, private ZenModeAddBypassingAppsPreferenceController(Context context,
@Nullable ApplicationsState appState, @Nullable Fragment host, @Nullable ApplicationsState appState, @Nullable Fragment host,
@Nullable NotificationBackend notificationBackend) { @Nullable NotificationBackend notificationBackend,
@Nullable ZenHelperBackend helperBackend) {
super(context); super(context);
mNotificationBackend = notificationBackend; mNotificationBackend = notificationBackend;
mApplicationsState = appState; mApplicationsState = appState;
mHostFragment = host; mHostFragment = host;
mHelperBackend = helperBackend;
mUserManager = context.getSystemService(UserManager.class);
} }
@Override @Override
@@ -147,7 +159,7 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere
@VisibleForTesting @VisibleForTesting
void updateAppList(List<ApplicationsState.AppEntry> apps) { void updateAppList(List<ApplicationsState.AppEntry> apps) {
if (apps == null) { if (apps == null || mNotificationBackend == null) {
return; return;
} }
@@ -157,21 +169,49 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere
mPreferenceScreen.addPreference(mPreferenceCategory); 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; boolean doAnyAppsPassCriteria = false;
for (ApplicationsState.AppEntry app : apps) { for (ApplicationsState.AppEntry app : apps) {
String pkg = app.info.packageName; String pkg = app.info.packageName;
final String key = getKey(pkg, app.info.uid); final String key = getKey(pkg, app.info.uid);
final int appChannels = mNotificationBackend.getChannelCount(pkg, app.info.uid); int userId = UserHandle.getUserId(app.info.uid);
final int appChannelsBypassingDnd = mNotificationBackend
.getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size(); boolean doesAppBypassDnd, doesAppHaveAnyChannels;
if (appChannelsBypassingDnd == 0 && appChannels > 0) { 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; doAnyAppsPassCriteria = true;
} }
Preference pref = mPreferenceCategory.findPreference(key); Preference pref = mPreferenceCategory.findPreference(key);
if (pref == null) { if (pref == null) {
if (appChannelsBypassingDnd == 0 && appChannels > 0) { if (!doesAppBypassDnd && doesAppHaveAnyChannels) {
// does not exist but should // does not exist but should
pref = new AppPreference(mPrefContext); pref = new AppPreference(mPrefContext);
pref.setKey(key); pref.setKey(key);
@@ -193,7 +233,7 @@ public class ZenModeAddBypassingAppsPreferenceController extends AbstractPrefere
updateIcon(pref, app); updateIcon(pref, app);
mPreferenceCategory.addPreference(pref); mPreferenceCategory.addPreference(pref);
} }
} else if (appChannelsBypassingDnd != 0 || appChannels == 0) { } else if (doesAppBypassDnd || !doesAppHaveAnyChannels) {
// exists but shouldn't anymore // exists but shouldn't anymore
mPreferenceCategory.removePreference(pref); mPreferenceCategory.removePreference(pref);
} }

View File

@@ -60,7 +60,7 @@ public class ZenModeSelectBypassingAppsFragment extends ZenModeFragmentBase impl
controllers.add(new ZenModeAllBypassingAppsPreferenceController(context, app, host, controllers.add(new ZenModeAllBypassingAppsPreferenceController(context, app, host,
zenHelperBackend)); zenHelperBackend));
controllers.add(new ZenModeAddBypassingAppsPreferenceController(context, app, host, controllers.add(new ZenModeAddBypassingAppsPreferenceController(context, app, host,
notificationBackend)); notificationBackend, zenHelperBackend));
return controllers; return controllers;
} }

View File

@@ -19,6 +19,7 @@ package com.android.settings.notification.modes;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -30,6 +31,8 @@ import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.ParceledListSlice; import android.content.pm.ParceledListSlice;
import android.platform.test.annotations.EnableFlags; 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 android.platform.test.flag.junit.SetFlagsRule;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
@@ -46,36 +49,50 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner; import org.robolectric.ParameterizedRobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; 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) @EnableFlags(Flags.FLAG_MODES_UI)
public class ZenModeAddBypassingAppsPreferenceControllerTest { public class ZenModeAddBypassingAppsPreferenceControllerTest {
@Rule @Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock @Mock
private NotificationBackend mBackend; private NotificationBackend mBackend;
@Mock @Mock
private ZenHelperBackend mHelperBackend;
@Mock
private PreferenceCategory mPreferenceCategory; private PreferenceCategory mPreferenceCategory;
@Mock @Mock
private ApplicationsState mApplicationState; private ApplicationsState mApplicationState;
private ZenModeAddBypassingAppsPreferenceController mController; private ZenModeAddBypassingAppsPreferenceController mController;
private Context mContext; 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 @Before
public void setup() { public void setup() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application; mContext = RuntimeEnvironment.application;
mController = new ZenModeAddBypassingAppsPreferenceController( mController = new ZenModeAddBypassingAppsPreferenceController(
mContext, null, mock(Fragment.class), mBackend); mContext, null, mock(Fragment.class), mBackend, mHelperBackend);
mController.mPreferenceCategory = mPreferenceCategory; mController.mPreferenceCategory = mPreferenceCategory;
mController.mApplicationsState = mApplicationState; mController.mApplicationsState = mApplicationState;
mController.mPrefContext = mContext; mController.mPrefContext = mContext;
@@ -132,6 +149,12 @@ public class ZenModeAddBypassingAppsPreferenceControllerTest {
appWithChannelsNoneBypassing.info.uid)) appWithChannelsNoneBypassing.info.uid))
.thenReturn(new ParceledListSlice<>(new ArrayList<>())); .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<>(); List<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
appEntries.add(appWithBypassingChannels); appEntries.add(appWithBypassingChannels);
appEntries.add(appWithoutChannels); appEntries.add(appWithoutChannels);