Merge "reduce number of binder calls when loading page" into main

This commit is contained in:
Julia Reynolds
2024-11-12 22:00:26 +00:00
committed by Android (Google) Code Review
6 changed files with 80 additions and 58 deletions

View File

@@ -18,6 +18,7 @@ package com.android.settings.notification.modes;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.INotificationManager; import android.app.INotificationManager;
import android.app.ZenBypassingApp;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.Context; import android.content.Context;
import android.content.pm.ParceledListSlice; import android.content.pm.ParceledListSlice;
@@ -30,6 +31,7 @@ import android.os.UserManager;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Contacts;
import android.service.notification.ConversationChannelWrapper; import android.service.notification.ConversationChannelWrapper;
import android.util.ArrayMap;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -41,8 +43,9 @@ import com.google.common.collect.ImmutableList;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
/** /**
@@ -76,16 +79,20 @@ class ZenHelperBackend {
} }
/** /**
* Returns all of a user's packages that have at least one channel that will bypass DND * Returns a mapping between a user's packages that have at least one channel that will
* bypass DND, and a Boolean indicating whether all of the package's channels bypass.
*/ */
List<String> getPackagesBypassingDnd(int userId, Map<String, Boolean> getPackagesBypassingDnd(int userId) {
boolean includeConversationChannels) { Map<String, Boolean> bypassingAppsMap = new HashMap<>();
try { try {
return mInm.getPackagesBypassingDnd(userId, includeConversationChannels); List<ZenBypassingApp> bypassingApps = mInm.getPackagesBypassingDnd(userId).getList();
for (ZenBypassingApp zba : bypassingApps) {
bypassingAppsMap.put(zba.getPkg(), zba.doAllChannelsBypass());
}
} catch (Exception e) { } catch (Exception e) {
Log.w(TAG, "Error calling NoMan", e); Log.w(TAG, "Error calling NoMan", e);
return new ArrayList<>();
} }
return bypassingAppsMap;
} }
/** Returns all conversation channels for profiles of the current user. */ /** Returns all conversation channels for profiles of the current user. */

View File

@@ -22,7 +22,9 @@ 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.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.core.text.BidiFormatter; import androidx.core.text.BidiFormatter;
@@ -35,7 +37,6 @@ import com.android.settings.R;
import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.AppInfoBase;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.notification.app.AppChannelsBypassingDndSettings; import com.android.settings.notification.app.AppChannelsBypassingDndSettings;
import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState;
@@ -44,7 +45,9 @@ 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.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Adds a preference to the PreferenceScreen for each notification channel that can bypass DND. * Adds a preference to the PreferenceScreen for each notification channel that can bypass DND.
@@ -54,7 +57,8 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
public static final String KEY_NO_APPS = "all_none"; public static final String KEY_NO_APPS = "all_none";
private static final String KEY = "zen_mode_bypassing_apps_list"; private static final String KEY = "zen_mode_bypassing_apps_list";
@Nullable private final NotificationBackend mNotificationBackend; @Nullable private final ZenHelperBackend mHelperBackend;
private final UserManager mUserManager;
@Nullable @VisibleForTesting ApplicationsState mApplicationsState; @Nullable @VisibleForTesting ApplicationsState mApplicationsState;
@VisibleForTesting PreferenceCategory mPreferenceCategory; @VisibleForTesting PreferenceCategory mPreferenceCategory;
@@ -64,18 +68,18 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
@Nullable private Fragment mHostFragment; @Nullable private Fragment mHostFragment;
public ZenModeAllBypassingAppsPreferenceController(Context context, @Nullable Application app, public ZenModeAllBypassingAppsPreferenceController(Context context, @Nullable Application app,
@Nullable Fragment host, @Nullable NotificationBackend notificationBackend) { @Nullable Fragment host, @Nullable ZenHelperBackend helperBackend) {
this(context, app == null ? null : ApplicationsState.getInstance(app), host, this(context, app == null ? null : ApplicationsState.getInstance(app), host, helperBackend);
notificationBackend);
} }
private ZenModeAllBypassingAppsPreferenceController(Context context, private ZenModeAllBypassingAppsPreferenceController(Context context,
@Nullable ApplicationsState appState, @Nullable Fragment host, @Nullable ApplicationsState appState, @Nullable Fragment host,
@Nullable NotificationBackend notificationBackend) { @Nullable ZenHelperBackend helperBackend) {
super(context); super(context);
mNotificationBackend = notificationBackend;
mApplicationsState = appState; mApplicationsState = appState;
mHostFragment = host; mHostFragment = host;
mHelperBackend = helperBackend;
mUserManager = context.getSystemService(UserManager.class);
if (mApplicationsState != null && host != null) { if (mApplicationsState != null && host != null) {
mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, host.getLifecycle()); mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, host.getLifecycle());
@@ -140,19 +144,25 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
} }
boolean doAnyAppsPassCriteria = false; boolean doAnyAppsPassCriteria = false;
Map<Integer, Map<String, Boolean>> packagesBypassingDndByUser = new HashMap<>();
for (UserHandle userHandle : mUserManager.getUserProfiles()) {
packagesBypassingDndByUser.put(userHandle.getIdentifier(),
mHelperBackend.getPackagesBypassingDnd(userHandle.getIdentifier()));
}
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); boolean doesAppBypassDnd = false;
final int appChannelsBypassingDnd = mNotificationBackend int userId = UserHandle.getUserId(app.info.uid);
.getNotificationChannelsBypassingDnd(pkg, app.info.uid).getList().size(); Map<String, Boolean> packagesBypassingDnd =
if (appChannelsBypassingDnd > 0) { packagesBypassingDndByUser.getOrDefault(userId, new HashMap<>());
if (packagesBypassingDnd.containsKey(pkg)) {
doAnyAppsPassCriteria = true; doAnyAppsPassCriteria = true;
doesAppBypassDnd = true;
} }
Preference pref = mPreferenceCategory.findPreference(key); Preference pref = mPreferenceCategory.findPreference(key);
if (pref == null) { if (pref == null) {
if (appChannelsBypassingDnd > 0) { if (doesAppBypassDnd) {
// does not exist but should // does not exist but should
pref = new AppPreference(mPrefContext); pref = new AppPreference(mPrefContext);
pref.setKey(key); pref.setKey(key);
@@ -172,14 +182,14 @@ public class ZenModeAllBypassingAppsPreferenceController extends AbstractPrefere
}); });
pref.setTitle(BidiFormatter.getInstance().unicodeWrap(app.label)); pref.setTitle(BidiFormatter.getInstance().unicodeWrap(app.label));
updateIcon(pref, app); updateIcon(pref, app);
if (appChannels > appChannelsBypassingDnd) { if (packagesBypassingDnd.get(pkg)) {
pref.setSummary(R.string.zen_mode_bypassing_apps_summary_some);
} else {
pref.setSummary(R.string.zen_mode_bypassing_apps_summary_all); pref.setSummary(R.string.zen_mode_bypassing_apps_summary_all);
} else {
pref.setSummary(R.string.zen_mode_bypassing_apps_summary_some);
} }
mPreferenceCategory.addPreference(pref); mPreferenceCategory.addPreference(pref);
} }
} else if (appChannelsBypassingDnd == 0) { } else if (!doesAppBypassDnd) {
// exists but shouldn't anymore // exists but shouldn't anymore
mPreferenceCategory.removePreference(pref); mPreferenceCategory.removePreference(pref);
} }

View File

@@ -161,8 +161,7 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
Multimap<Integer, String> packagesBypassingDnd = HashMultimap.create(); Multimap<Integer, String> packagesBypassingDnd = HashMultimap.create();
for (UserHandle userHandle : mUserManager.getUserProfiles()) { for (UserHandle userHandle : mUserManager.getUserProfiles()) {
packagesBypassingDnd.putAll(userHandle.getIdentifier(), packagesBypassingDnd.putAll(userHandle.getIdentifier(),
mHelperBackend.getPackagesBypassingDnd(userHandle.getIdentifier(), mHelperBackend.getPackagesBypassingDnd(userHandle.getIdentifier()).keySet());
/* includeConversationChannels= */ false));
} }
return ImmutableList.copyOf( return ImmutableList.copyOf(

View File

@@ -48,15 +48,17 @@ public class ZenModeSelectBypassingAppsFragment extends ZenModeFragmentBase impl
} else { } else {
app = null; app = null;
} }
return buildPreferenceControllers(context, app, this, new NotificationBackend()); return buildPreferenceControllers(context, app, this, new NotificationBackend(),
new ZenHelperBackend(context));
} }
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context, private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
@Nullable Application app, @Nullable Fragment host, @Nullable Application app, @Nullable Fragment host,
@Nullable NotificationBackend notificationBackend) { @Nullable NotificationBackend notificationBackend,
@Nullable ZenHelperBackend zenHelperBackend) {
final List<AbstractPreferenceController> controllers = new ArrayList<>(); final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new ZenModeAllBypassingAppsPreferenceController(context, app, host, controllers.add(new ZenModeAllBypassingAppsPreferenceController(context, app, host,
notificationBackend)); zenHelperBackend));
controllers.add(new ZenModeAddBypassingAppsPreferenceController(context, app, host, controllers.add(new ZenModeAddBypassingAppsPreferenceController(context, app, host,
notificationBackend)); notificationBackend));
return controllers; return controllers;
@@ -86,7 +88,7 @@ public class ZenModeSelectBypassingAppsFragment extends ZenModeFragmentBase impl
@Override @Override
public List<AbstractPreferenceController> createPreferenceControllers( public List<AbstractPreferenceController> createPreferenceControllers(
Context context) { Context context) {
return buildPreferenceControllers(context, null, null, null); return buildPreferenceControllers(context, null, null, null, null);
} }
}; };
} }

View File

@@ -20,7 +20,6 @@ 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.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
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.times; import static org.mockito.Mockito.times;
@@ -28,10 +27,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.Flags; import android.app.Flags;
import android.app.NotificationChannel;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.ParceledListSlice;
import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.junit.SetFlagsRule;
@@ -39,7 +36,6 @@ import androidx.fragment.app.Fragment;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceCategory;
import com.android.settings.notification.NotificationBackend;
import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState;
import org.junit.Before; import org.junit.Before;
@@ -54,6 +50,7 @@ import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@EnableFlags(Flags.FLAG_MODES_UI) @EnableFlags(Flags.FLAG_MODES_UI)
@@ -66,7 +63,7 @@ public class ZenModeAllBypassingAppsPreferenceControllerTest {
private Context mContext; private Context mContext;
@Mock @Mock
private NotificationBackend mBackend; private ZenHelperBackend mBackend;
@Mock @Mock
private PreferenceCategory mPreferenceCategory; private PreferenceCategory mPreferenceCategory;
@Mock @Mock
@@ -102,18 +99,25 @@ public class ZenModeAllBypassingAppsPreferenceControllerTest {
entry2.info.packageName = "test2"; entry2.info.packageName = "test2";
entry2.info.uid = 0; entry2.info.uid = 0;
ApplicationsState.AppEntry entry3= mock(ApplicationsState.AppEntry.class);
entry3.info = new ApplicationInfo();
entry3.info.packageName = "test3";
entry3.info.uid = 0;
List<ApplicationsState.AppEntry> appEntries = new ArrayList<>(); List<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
appEntries.add(entry1); appEntries.add(entry1);
appEntries.add(entry2); appEntries.add(entry2);
List<NotificationChannel> channelsBypassing = new ArrayList<>(); appEntries.add(entry3);
channelsBypassing.add(mock(NotificationChannel.class)); when(mBackend.getPackagesBypassingDnd(anyInt())).thenReturn(
channelsBypassing.add(mock(NotificationChannel.class)); Map.of("test", true, "test2", false));
when(mBackend.getNotificationChannelsBypassingDnd(anyString(),
anyInt())).thenReturn(new ParceledListSlice<>(channelsBypassing));
// THEN there's are two preferences // THEN there's are two preferences
mController.updateAppList(appEntries); mController.updateAppList(appEntries);
verify(mPreferenceCategory, times(2)).addPreference(any()); ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
verify(mPreferenceCategory, times(2)).addPreference(captor.capture());
List<Preference> prefs = captor.getAllValues();
assertThat(prefs.get(0).getSummary().toString()).isEqualTo("All notifications");
assertThat(prefs.get(1).getSummary().toString()).isEqualTo("Some notifications");
} }
@Test @Test

View File

@@ -77,6 +77,7 @@ import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Random; import java.util.Random;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@@ -200,8 +201,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
ApplicationsState.AppEntry app2 = createAppEntry("app2", mContext.getUserId()); ApplicationsState.AppEntry app2 = createAppEntry("app2", mContext.getUserId());
List<ApplicationsState.AppEntry> allApps = List.of(app1, app2); List<ApplicationsState.AppEntry> allApps = List.of(app1, app2);
when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId(), when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId())).thenReturn(
false)).thenReturn(List.of("app1")); Map.of("app1", true));
assertThat(mController.getAppsBypassingDndSortedByName(allApps)).containsExactly(app1); assertThat(mController.getAppsBypassingDndSortedByName(allApps)).containsExactly(app1);
} }
@@ -213,8 +214,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
ApplicationsState.AppEntry appB = createAppEntry("B", mContext.getUserId()); ApplicationsState.AppEntry appB = createAppEntry("B", mContext.getUserId());
List<ApplicationsState.AppEntry> allApps = List.of(appC, appA, appB); List<ApplicationsState.AppEntry> allApps = List.of(appC, appA, appB);
when(mHelperBackend.getPackagesBypassingDnd(eq(mContext.getUserId()), anyBoolean())) when(mHelperBackend.getPackagesBypassingDnd(eq(mContext.getUserId())))
.thenReturn(List.of("B", "C", "A")); .thenReturn(Map.of("B", true, "C", false, "A", true));
assertThat(mController.getAppsBypassingDndSortedByName(allApps)) assertThat(mController.getAppsBypassingDndSortedByName(allApps))
.containsExactly(appA, appB, appC).inOrder(); .containsExactly(appA, appB, appC).inOrder();
@@ -234,10 +235,10 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
List<ApplicationsState.AppEntry> allApps = List.of(workCopy, personalCopy, otherPersonal, List<ApplicationsState.AppEntry> allApps = List.of(workCopy, personalCopy, otherPersonal,
otherWork); otherWork);
when(mHelperBackend.getPackagesBypassingDnd(eq(mContext.getUserId()), anyBoolean())) when(mHelperBackend.getPackagesBypassingDnd(eq(mContext.getUserId())))
.thenReturn(List.of("app", "p2")); .thenReturn(Map.of("app", true, "p2", true));
when(mHelperBackend.getPackagesBypassingDnd(eq(10), anyBoolean())) when(mHelperBackend.getPackagesBypassingDnd(eq(10)))
.thenReturn(List.of("app")); .thenReturn(Map.of("app", false));
// Personal copy before work copy (names match). // Personal copy before work copy (names match).
assertThat(mController.getAppsBypassingDndSortedByName(allApps)) assertThat(mController.getAppsBypassingDndSortedByName(allApps))
@@ -253,7 +254,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
mController.updateState(mPreference, zenMode); mController.updateState(mPreference, zenMode);
verifyNoMoreInteractions(mSession); verifyNoMoreInteractions(mSession);
verify(mHelperBackend, never()).getPackagesBypassingDnd(anyInt(), anyBoolean()); verify(mHelperBackend, never()).getPackagesBypassingDnd(anyInt());
assertThat(String.valueOf(mPreference.getSummary())).isEqualTo("None"); assertThat(String.valueOf(mPreference.getSummary())).isEqualTo("None");
} }
@@ -266,9 +267,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
ArrayList<ApplicationsState.AppEntry> appEntries = new ArrayList<>(); ArrayList<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
appEntries.add(createAppEntry("test", mContext.getUserId())); appEntries.add(createAppEntry("test", mContext.getUserId()));
when(mHelperBackend.getPackagesBypassingDnd( when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId()))
mContext.getUserId(), false)) .thenReturn(Map.of("test", false));
.thenReturn(List.of("test"));
// Updates the preference with the zen mode. We expect that this causes the app session // Updates the preference with the zen mode. We expect that this causes the app session
// to trigger a rebuild (and display a temporary text in the meantime). // to trigger a rebuild (and display a temporary text in the meantime).
@@ -286,8 +286,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
ZenMode zenMode = createPriorityChannelsZenMode(); ZenMode zenMode = createPriorityChannelsZenMode();
mController.updateState(mPreference, zenMode); mController.updateState(mPreference, zenMode);
when(mHelperBackend.getPackagesBypassingDnd(anyInt(), anyBoolean())) when(mHelperBackend.getPackagesBypassingDnd(anyInt()))
.thenReturn(ImmutableList.of("test1", "test2")); .thenReturn(Map.of("test1", false, "test2", false));
ArrayList<ApplicationsState.AppEntry> appEntries = new ArrayList<>(); ArrayList<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
appEntries.add(createAppEntry("test1", mContext.getUserId())); appEntries.add(createAppEntry("test1", mContext.getUserId()));
appEntries.add(createAppEntry("test2", mContext.getUserId())); appEntries.add(createAppEntry("test2", mContext.getUserId()));
@@ -328,8 +328,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
.build(); .build();
ArrayList<ApplicationsState.AppEntry> appEntries = new ArrayList<>(); ArrayList<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
appEntries.add(createAppEntry("test", mContext.getUserId())); appEntries.add(createAppEntry("test", mContext.getUserId()));
when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId(), false)) when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId()))
.thenReturn(List.of("test")); .thenReturn(Map.of("test", true));
mController.updateState(mPreference, zenModeWithNone); mController.updateState(mPreference, zenModeWithNone);
@@ -355,8 +355,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
.build(); .build();
ArrayList<ApplicationsState.AppEntry> appEntries = new ArrayList<>(); ArrayList<ApplicationsState.AppEntry> appEntries = new ArrayList<>();
appEntries.add(createAppEntry("test", mContext.getUserId())); appEntries.add(createAppEntry("test", mContext.getUserId()));
when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId(), false)) when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId()))
.thenReturn(List.of("test")); .thenReturn(Map.of("test", true));
mController.updateState(mPreference, zenModeWithPriority); mController.updateState(mPreference, zenModeWithPriority);