Update ZenModeAddBypassingApps to not binder call for every app
This update already happened for ZenModeAllBypassingApps to use the new single binder call to get all packages bypassing DND. This change uses that method for bypassing apps as well as collecting the set of all apps with a nonzero number of channels. Bug: 368623163 Bug: 394614704 Test: ZenModeAddBypassingAppsPreferenceControllerTest, manual to confirm correct behavior Flag: android.app.nm_binder_perf_get_apps_with_channels Change-Id: I72a1edcb07d18f5707591a5341d7a7338c23f42b
This commit is contained in:
@@ -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;
|
||||||
@@ -54,6 +55,7 @@ import android.text.format.DateUtils;
|
|||||||
import android.util.IconDrawableFactory;
|
import android.util.IconDrawableFactory;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import com.android.internal.util.CollectionUtils;
|
import com.android.internal.util.CollectionUtils;
|
||||||
@@ -66,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";
|
||||||
@@ -367,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);
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
Reference in New Issue
Block a user