Merge "Revert "[People Service] Add throttling to DataManager add / change listener"" into tm-qpr-dev

This commit is contained in:
Jeff Nainaparampil
2023-03-01 04:54:15 +00:00
committed by Android (Google) Code Review
5 changed files with 36 additions and 214 deletions

View File

@@ -113,7 +113,6 @@ public class DataManager {
private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L; private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L;
@VisibleForTesting @VisibleForTesting
static final int MAX_CACHED_RECENT_SHORTCUTS = 30; static final int MAX_CACHED_RECENT_SHORTCUTS = 30;
private static final int DEBOUNCE_LENGTH_MS = 500;
private final Context mContext; private final Context mContext;
private final Injector mInjector; private final Injector mInjector;
@@ -130,7 +129,6 @@ public class DataManager {
private final List<PeopleService.ConversationsListener> mConversationsListeners = private final List<PeopleService.ConversationsListener> mConversationsListeners =
new ArrayList<>(1); new ArrayList<>(1);
private final Handler mHandler; private final Handler mHandler;
private final PerPackageThrottler mShortcutsThrottler;
private ContentObserver mCallLogContentObserver; private ContentObserver mCallLogContentObserver;
private ContentObserver mMmsSmsContentObserver; private ContentObserver mMmsSmsContentObserver;
@@ -142,17 +140,14 @@ public class DataManager {
private ConversationStatusExpirationBroadcastReceiver mStatusExpReceiver; private ConversationStatusExpirationBroadcastReceiver mStatusExpReceiver;
public DataManager(Context context) { public DataManager(Context context) {
this(context, new Injector(), BackgroundThread.get().getLooper(), this(context, new Injector(), BackgroundThread.get().getLooper());
new PerPackageThrottlerImpl(BackgroundThread.getHandler(), DEBOUNCE_LENGTH_MS));
} }
DataManager(Context context, Injector injector, Looper looper, DataManager(Context context, Injector injector, Looper looper) {
PerPackageThrottler shortcutsThrottler) {
mContext = context; mContext = context;
mInjector = injector; mInjector = injector;
mScheduledExecutor = mInjector.createScheduledExecutor(); mScheduledExecutor = mInjector.createScheduledExecutor();
mHandler = new Handler(looper); mHandler = new Handler(looper);
mShortcutsThrottler = shortcutsThrottler;
} }
/** Initialization. Called when the system services are up running. */ /** Initialization. Called when the system services are up running. */
@@ -1109,34 +1104,25 @@ public class DataManager {
@Override @Override
public void onShortcutsAddedOrUpdated(@NonNull String packageName, public void onShortcutsAddedOrUpdated(@NonNull String packageName,
@NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
mShortcutsThrottler.scheduleDebounced( mInjector.getBackgroundExecutor().execute(() -> {
new Pair<>(packageName, user.getIdentifier()),
() -> {
PackageData packageData = getPackage(packageName, user.getIdentifier()); PackageData packageData = getPackage(packageName, user.getIdentifier());
List<ShortcutInfo> queriedShortcuts = getShortcuts(packageName, for (ShortcutInfo shortcut : shortcuts) {
user.getIdentifier(), null);
boolean hasCachedShortcut = false;
for (ShortcutInfo shortcut : queriedShortcuts) {
if (ShortcutHelper.isConversationShortcut( if (ShortcutHelper.isConversationShortcut(
shortcut, mShortcutServiceInternal, user.getIdentifier())) { shortcut, mShortcutServiceInternal, user.getIdentifier())) {
if (shortcut.isCached()) { if (shortcut.isCached()) {
ConversationInfo info = packageData != null ConversationInfo conversationInfo = packageData != null
? packageData.getConversationInfo(shortcut.getId()) ? packageData.getConversationInfo(shortcut.getId()) : null;
: null; if (conversationInfo == null
if (info == null || !conversationInfo.isShortcutCachedForNotification()) {
|| !info.isShortcutCachedForNotification()) { // This is a newly cached shortcut. Clean up the existing cached
hasCachedShortcut = true; // shortcuts to ensure the cache size is under the limit.
cleanupCachedShortcuts(user.getIdentifier(),
MAX_CACHED_RECENT_SHORTCUTS - 1);
} }
} }
addOrUpdateConversationInfo(shortcut); addOrUpdateConversationInfo(shortcut);
} }
} }
// Added at least one new conversation. Uncache older existing cached
// shortcuts to ensure the cache size is under the limit.
if (hasCachedShortcut) {
cleanupCachedShortcuts(user.getIdentifier(),
MAX_CACHED_RECENT_SHORTCUTS);
}
}); });
} }

View File

@@ -1,28 +0,0 @@
/*
* Copyright (C) 2023 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 com.android.server.people.data;
import android.util.Pair;
/** The interface for throttling expensive runnables per package. */
interface PerPackageThrottler {
/**
* Schedule a runnable to run in the future, and debounce runnables for same {@code pkgUserId}
* that occur until that future has run.
*/
void scheduleDebounced(Pair<String, Integer> pkgUserId, Runnable runnable);
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright (C) 2023 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 com.android.server.people.data;
import android.os.Handler;
import android.util.Pair;
import java.util.HashSet;
/**
* A class that implements a per-package throttler that prevents a runnable from executing more than
* once every {@code debounceTime}.
*/
public class PerPackageThrottlerImpl implements PerPackageThrottler {
private final Handler mBackgroundHandler;
private final int mDebounceTime;
private final HashSet<Pair<String, Integer>> mPkgScheduledTasks = new HashSet<>();
PerPackageThrottlerImpl(Handler backgroundHandler, int debounceTime) {
mBackgroundHandler = backgroundHandler;
mDebounceTime = debounceTime;
}
@Override
public synchronized void scheduleDebounced(
Pair<String, Integer> pkgUserId, Runnable runnable) {
if (mPkgScheduledTasks.contains(pkgUserId)) {
return;
}
mPkgScheduledTasks.add(pkgUserId);
mBackgroundHandler.postDelayed(() -> {
synchronized (this) {
mPkgScheduledTasks.remove(pkgUserId);
runnable.run();
}
}, mDebounceTime);
}
}

View File

@@ -86,7 +86,6 @@ import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager; import android.telecom.TelecomManager;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.util.Pair;
import android.util.Range; import android.util.Range;
import com.android.internal.app.ChooserActivity; import com.android.internal.app.ChooserActivity;
@@ -187,7 +186,6 @@ public final class DataManagerTest {
private ShortcutInfo mShortcutInfo; private ShortcutInfo mShortcutInfo;
private TestInjector mInjector; private TestInjector mInjector;
private TestLooper mLooper; private TestLooper mLooper;
private TestPerPackageThrottler mShortcutThrottler;
@Before @Before
public void setUp() throws PackageManager.NameNotFoundException { public void setUp() throws PackageManager.NameNotFoundException {
@@ -277,9 +275,7 @@ public final class DataManagerTest {
mInjector = new TestInjector(); mInjector = new TestInjector();
mLooper = new TestLooper(); mLooper = new TestLooper();
mShortcutThrottler = new TestPerPackageThrottler(); mDataManager = new DataManager(mContext, mInjector, mLooper.getLooper());
mDataManager = new DataManager(mContext, mInjector, mLooper.getLooper(),
mShortcutThrottler);
mDataManager.initialize(); mDataManager.initialize();
when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
@@ -287,7 +283,10 @@ public final class DataManagerTest {
mShortcutInfo = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, mShortcutInfo = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson()); buildPerson());
mockGetShortcuts(Collections.singletonList(mShortcutInfo)); when(mShortcutServiceInternal.getShortcuts(
anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
anyInt(), anyInt(), anyInt(), anyInt()))
.thenReturn(Collections.singletonList(mShortcutInfo));
verify(mShortcutServiceInternal).addShortcutChangeCallback( verify(mShortcutServiceInternal).addShortcutChangeCallback(
mShortcutChangeCallbackCaptor.capture()); mShortcutChangeCallbackCaptor.capture());
mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue(); mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue();
@@ -973,7 +972,6 @@ public final class DataManagerTest {
buildPerson()); buildPerson());
ShortcutInfo shortcut3 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc3", ShortcutInfo shortcut3 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc3",
buildPerson()); buildPerson());
mockGetShortcuts(List.of(shortcut1, shortcut2, shortcut3));
mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME, mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME,
Arrays.asList(shortcut1, shortcut2, shortcut3), UserHandle.of(USER_ID_PRIMARY)); Arrays.asList(shortcut1, shortcut2, shortcut3), UserHandle.of(USER_ID_PRIMARY));
mShortcutChangeCallback.onShortcutsRemoved(TEST_PKG_NAME, mShortcutChangeCallback.onShortcutsRemoved(TEST_PKG_NAME,
@@ -1225,6 +1223,7 @@ public final class DataManagerTest {
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS)); eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
} }
} }
@Test @Test
public void testUncacheOldestCachedShortcut_missingNotificationEvents() { public void testUncacheOldestCachedShortcut_missingNotificationEvents() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY); mDataManager.onUserUnlocked(USER_ID_PRIMARY);
@@ -1234,7 +1233,6 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId, ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
buildPerson()); buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS); shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mockGetShortcuts(Collections.singletonList(shortcut));
mShortcutChangeCallback.onShortcutsAddedOrUpdated( mShortcutChangeCallback.onShortcutsAddedOrUpdated(
TEST_PKG_NAME, TEST_PKG_NAME,
Collections.singletonList(shortcut), Collections.singletonList(shortcut),
@@ -1254,6 +1252,7 @@ public final class DataManagerTest {
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS)); eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
} }
} }
@Test @Test
public void testUncacheOldestCachedShortcut_legacyConversation() { public void testUncacheOldestCachedShortcut_legacyConversation() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY); mDataManager.onUserUnlocked(USER_ID_PRIMARY);
@@ -1275,7 +1274,6 @@ public final class DataManagerTest {
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId, ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
buildPerson()); buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS); shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mockGetShortcuts(Collections.singletonList(shortcut));
mShortcutChangeCallback.onShortcutsAddedOrUpdated( mShortcutChangeCallback.onShortcutsAddedOrUpdated(
TEST_PKG_NAME, TEST_PKG_NAME,
Collections.singletonList(shortcut), Collections.singletonList(shortcut),
@@ -1313,8 +1311,7 @@ public final class DataManagerTest {
mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter); mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
byte[] payload = mDataManager.getBackupPayload(USER_ID_PRIMARY); byte[] payload = mDataManager.getBackupPayload(USER_ID_PRIMARY);
DataManager dataManager = new DataManager( DataManager dataManager = new DataManager(mContext, mInjector, mLooper.getLooper());
mContext, mInjector, mLooper.getLooper(), mShortcutThrottler);
dataManager.onUserUnlocked(USER_ID_PRIMARY); dataManager.onUserUnlocked(USER_ID_PRIMARY);
dataManager.restore(USER_ID_PRIMARY, payload); dataManager.restore(USER_ID_PRIMARY, payload);
ConversationInfo conversationInfo = dataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY) ConversationInfo conversationInfo = dataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
@@ -1726,13 +1723,6 @@ public final class DataManagerTest {
return (queryFlags & flag) != 0; return (queryFlags & flag) != 0;
} }
private void mockGetShortcuts(List<ShortcutInfo> shortcutInfoList) {
when(mShortcutServiceInternal.getShortcuts(
anyInt(), anyString(), anyLong(), anyString(), any(), any(), any(),
anyInt(), anyInt(), anyInt(), anyInt()))
.thenReturn(shortcutInfoList);
}
// "Sends" a notification to a non-customized notification channel - the notification channel // "Sends" a notification to a non-customized notification channel - the notification channel
// is something generic like "messages" and the notification has a shortcut id // is something generic like "messages" and the notification has a shortcut id
private void sendGenericNotification() { private void sendGenericNotification() {
@@ -1958,11 +1948,4 @@ public final class DataManagerTest {
return mUsageStatsQueryHelper; return mUsageStatsQueryHelper;
} }
} }
private static class TestPerPackageThrottler implements PerPackageThrottler {
@Override
public void scheduleDebounced(Pair<String, Integer> pkgUserId, Runnable runnable) {
runnable.run();
}
}
} }

View File

@@ -1,67 +0,0 @@
/*
* Copyright (C) 2023 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 com.android.server.people.data;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.util.Pair;
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.concurrent.atomic.AtomicBoolean;
@RunWith(JUnit4.class)
public class PerPackageThrottlerImplTest {
private static final int DEBOUNCE_INTERVAL = 500;
private static final String PKG_ONE = "pkg_one";
private static final String PKG_TWO = "pkg_two";
private static final int USER_ID = 10;
private final OffsettableClock mClock = new OffsettableClock.Stopped();
private final TestHandler mTestHandler = new TestHandler(null, mClock);
private PerPackageThrottlerImpl mThrottler;
@Before
public void setUp() {
mThrottler = new PerPackageThrottlerImpl(mTestHandler, DEBOUNCE_INTERVAL);
}
@Test
public void scheduleDebounced() {
AtomicBoolean pkgOneRan = new AtomicBoolean();
AtomicBoolean pkgTwoRan = new AtomicBoolean();
mThrottler.scheduleDebounced(new Pair<>(PKG_ONE, USER_ID), () -> pkgOneRan.set(true));
mThrottler.scheduleDebounced(new Pair<>(PKG_ONE, USER_ID), () -> pkgOneRan.set(true));
mThrottler.scheduleDebounced(new Pair<>(PKG_TWO, USER_ID), () -> pkgTwoRan.set(true));
mThrottler.scheduleDebounced(new Pair<>(PKG_TWO, USER_ID), () -> pkgTwoRan.set(true));
assertFalse(pkgOneRan.get());
assertFalse(pkgTwoRan.get());
mClock.fastForward(DEBOUNCE_INTERVAL);
mTestHandler.timeAdvance();
assertTrue(pkgOneRan.get());
assertTrue(pkgTwoRan.get());
}
}