|
| 1 | +From 42c9c06f9c0f8d7212284c6555e3ffd25bd4ddbf Mon Sep 17 00:00:00 2001 |
| 2 | +From: Pinyao Ting < [email protected]> |
| 3 | +Date: Thu, 30 Nov 2023 23:12:39 +0000 |
| 4 | +Subject: [PATCH] Added throttle when reporting shortcut usage |
| 5 | + |
| 6 | +Bug: 304290201 |
| 7 | +Test: manual |
| 8 | +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:bd88f35c6797b1795d1150af92760531ff73f14f) |
| 9 | +Merged-In: I96370cbd4f6a55f894c1a93307e5f82dfd394652 |
| 10 | +Change-Id: I96370cbd4f6a55f894c1a93307e5f82dfd394652 |
| 11 | +--- |
| 12 | + .../android/server/pm/ShortcutPackage.java | 32 +++++++++++ |
| 13 | + .../android/server/pm/ShortcutService.java | 18 ++---- |
| 14 | + .../server/pm/ShortcutManagerTest1.java | 55 ++++++++++++++++++- |
| 15 | + .../server/pm/ShortcutManagerTest2.java | 2 + |
| 16 | + 4 files changed, 92 insertions(+), 15 deletions(-) |
| 17 | + |
| 18 | +diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java |
| 19 | +index 5a662d9f3139..3b3c11e1209d 100644 |
| 20 | +--- a/services/core/java/com/android/server/pm/ShortcutPackage.java |
| 21 | ++++ b/services/core/java/com/android/server/pm/ShortcutPackage.java |
| 22 | +@@ -33,6 +33,7 @@ |
| 23 | + import android.app.appsearch.SearchResults; |
| 24 | + import android.app.appsearch.SearchSpec; |
| 25 | + import android.app.appsearch.SetSchemaRequest; |
| 26 | ++import android.app.usage.UsageStatsManagerInternal; |
| 27 | + import android.content.ComponentName; |
| 28 | + import android.content.Intent; |
| 29 | + import android.content.IntentFilter; |
| 30 | +@@ -47,6 +48,7 @@ |
| 31 | + import android.os.Binder; |
| 32 | + import android.os.PersistableBundle; |
| 33 | + import android.os.StrictMode; |
| 34 | ++import android.os.SystemClock; |
| 35 | + import android.text.format.Formatter; |
| 36 | + import android.util.ArrayMap; |
| 37 | + import android.util.ArraySet; |
| 38 | +@@ -160,6 +162,9 @@ class ShortcutPackage extends ShortcutPackageItem { |
| 39 | + private static final String KEY_BITMAPS = "bitmaps"; |
| 40 | + private static final String KEY_BITMAP_BYTES = "bitmapBytes"; |
| 41 | + |
| 42 | ++ @VisibleForTesting |
| 43 | ++ public static final int REPORT_USAGE_BUFFER_SIZE = 3; |
| 44 | ++ |
| 45 | + private final Executor mExecutor; |
| 46 | + |
| 47 | + /** |
| 48 | +@@ -195,6 +200,9 @@ class ShortcutPackage extends ShortcutPackageItem { |
| 49 | + |
| 50 | + private long mLastKnownForegroundElapsedTime; |
| 51 | + |
| 52 | ++ @GuardedBy("mLock") |
| 53 | ++ private List<Long> mLastReportedTime = new ArrayList<>(); |
| 54 | ++ |
| 55 | + @GuardedBy("mLock") |
| 56 | + private boolean mIsAppSearchSchemaUpToDate; |
| 57 | + |
| 58 | +@@ -1677,6 +1685,30 @@ public boolean hasNonManifestShortcuts() { |
| 59 | + return condition[0]; |
| 60 | + } |
| 61 | + |
| 62 | ++ void reportShortcutUsed(@NonNull final UsageStatsManagerInternal usageStatsManagerInternal, |
| 63 | ++ @NonNull final String shortcutId) { |
| 64 | ++ synchronized (mLock) { |
| 65 | ++ final long currentTS = SystemClock.elapsedRealtime(); |
| 66 | ++ final ShortcutService s = mShortcutUser.mService; |
| 67 | ++ if (mLastReportedTime.isEmpty() |
| 68 | ++ || mLastReportedTime.size() < REPORT_USAGE_BUFFER_SIZE) { |
| 69 | ++ mLastReportedTime.add(currentTS); |
| 70 | ++ } else if (currentTS - mLastReportedTime.get(0) > s.mSaveDelayMillis) { |
| 71 | ++ mLastReportedTime.remove(0); |
| 72 | ++ mLastReportedTime.add(currentTS); |
| 73 | ++ } else { |
| 74 | ++ return; |
| 75 | ++ } |
| 76 | ++ final long token = s.injectClearCallingIdentity(); |
| 77 | ++ try { |
| 78 | ++ usageStatsManagerInternal.reportShortcutUsage(getPackageName(), shortcutId, |
| 79 | ++ getUser().getUserId()); |
| 80 | ++ } finally { |
| 81 | ++ s.injectRestoreCallingIdentity(token); |
| 82 | ++ } |
| 83 | ++ } |
| 84 | ++ } |
| 85 | ++ |
| 86 | + public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) { |
| 87 | + pw.println(); |
| 88 | + |
| 89 | +diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java |
| 90 | +index 7fd32d2a6b1b..84eb799e31ff 100644 |
| 91 | +--- a/services/core/java/com/android/server/pm/ShortcutService.java |
| 92 | ++++ b/services/core/java/com/android/server/pm/ShortcutService.java |
| 93 | +@@ -365,7 +365,7 @@ public boolean test(PackageInfo pi) { |
| 94 | + private CompressFormat mIconPersistFormat; |
| 95 | + private int mIconPersistQuality; |
| 96 | + |
| 97 | +- private int mSaveDelayMillis; |
| 98 | ++ int mSaveDelayMillis; |
| 99 | + |
| 100 | + private final IPackageManager mIPackageManager; |
| 101 | + private final PackageManagerInternal mPackageManagerInternal; |
| 102 | +@@ -2269,7 +2269,7 @@ public void pushDynamicShortcut(String packageName, ShortcutInfo shortcut, |
| 103 | + |
| 104 | + packageShortcutsChanged(ps, changedShortcuts, removedShortcuts); |
| 105 | + |
| 106 | +- reportShortcutUsedInternal(packageName, shortcut.getId(), userId); |
| 107 | ++ ps.reportShortcutUsed(mUsageStatsManagerInternal, shortcut.getId()); |
| 108 | + |
| 109 | + verifyStates(); |
| 110 | + } |
| 111 | +@@ -2676,25 +2676,17 @@ public void reportShortcutUsed(String packageName, String shortcutId, int userId |
| 112 | + Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d", |
| 113 | + shortcutId, packageName, userId)); |
| 114 | + } |
| 115 | ++ final ShortcutPackage ps; |
| 116 | + synchronized (mLock) { |
| 117 | + throwIfUserLockedL(userId); |
| 118 | +- final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); |
| 119 | ++ ps = getPackageShortcutsForPublisherLocked(packageName, userId); |
| 120 | + if (ps.findShortcutById(shortcutId) == null) { |
| 121 | + Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s", |
| 122 | + packageName, shortcutId)); |
| 123 | + return; |
| 124 | + } |
| 125 | + } |
| 126 | +- reportShortcutUsedInternal(packageName, shortcutId, userId); |
| 127 | +- } |
| 128 | +- |
| 129 | +- private void reportShortcutUsedInternal(String packageName, String shortcutId, int userId) { |
| 130 | +- final long token = injectClearCallingIdentity(); |
| 131 | +- try { |
| 132 | +- mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId); |
| 133 | +- } finally { |
| 134 | +- injectRestoreCallingIdentity(token); |
| 135 | +- } |
| 136 | ++ ps.reportShortcutUsed(mUsageStatsManagerInternal, shortcutId); |
| 137 | + } |
| 138 | + |
| 139 | + @Override |
| 140 | +diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java |
| 141 | +index 867890f938ba..0f00fb877afe 100644 |
| 142 | +--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java |
| 143 | ++++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java |
| 144 | +@@ -402,8 +402,8 @@ public void testAddDynamicShortcuts() { |
| 145 | + |
| 146 | + public void testPushDynamicShortcut() { |
| 147 | + // Change the max number of shortcuts. |
| 148 | +- mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5"); |
| 149 | +- |
| 150 | ++ mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5," |
| 151 | ++ + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1"); |
| 152 | + setCaller(CALLING_PACKAGE_1, USER_0); |
| 153 | + |
| 154 | + final ShortcutInfo s1 = makeShortcut("s1"); |
| 155 | +@@ -541,6 +541,57 @@ public void testPushDynamicShortcut() { |
| 156 | + eq(CALLING_PACKAGE_1), eq("s9"), eq(USER_0)); |
| 157 | + } |
| 158 | + |
| 159 | ++ public void testPushDynamicShortcut_CallsToUsageStatsManagerAreThrottled() |
| 160 | ++ throws InterruptedException { |
| 161 | ++ mService.updateConfigurationLocked( |
| 162 | ++ ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=500"); |
| 163 | ++ |
| 164 | ++ // Verify calls to UsageStatsManagerInternal#reportShortcutUsage are throttled. |
| 165 | ++ setCaller(CALLING_PACKAGE_1, USER_0); |
| 166 | ++ for (int i = 0; i < ShortcutPackage.REPORT_USAGE_BUFFER_SIZE; i++) { |
| 167 | ++ final ShortcutInfo si = makeShortcut("s" + i); |
| 168 | ++ mManager.pushDynamicShortcut(si); |
| 169 | ++ } |
| 170 | ++ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage( |
| 171 | ++ eq(CALLING_PACKAGE_1), eq("s1"), eq(USER_0)); |
| 172 | ++ Mockito.reset(mMockUsageStatsManagerInternal); |
| 173 | ++ for (int i = ShortcutPackage.REPORT_USAGE_BUFFER_SIZE; i <= 10; i++) { |
| 174 | ++ final ShortcutInfo si = makeShortcut("s" + i); |
| 175 | ++ mManager.pushDynamicShortcut(si); |
| 176 | ++ } |
| 177 | ++ verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage( |
| 178 | ++ any(), any(), anyInt()); |
| 179 | ++ |
| 180 | ++ // Verify pkg2 isn't blocked by pkg1, but consecutive calls from pkg2 are throttled as well. |
| 181 | ++ setCaller(CALLING_PACKAGE_2, USER_0); |
| 182 | ++ for (int i = 0; i < ShortcutPackage.REPORT_USAGE_BUFFER_SIZE; i++) { |
| 183 | ++ final ShortcutInfo si = makeShortcut("s" + i); |
| 184 | ++ mManager.pushDynamicShortcut(si); |
| 185 | ++ } |
| 186 | ++ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage( |
| 187 | ++ eq(CALLING_PACKAGE_2), eq("s1"), eq(USER_0)); |
| 188 | ++ Mockito.reset(mMockUsageStatsManagerInternal); |
| 189 | ++ for (int i = ShortcutPackage.REPORT_USAGE_BUFFER_SIZE; i <= 10; i++) { |
| 190 | ++ final ShortcutInfo si = makeShortcut("s" + i); |
| 191 | ++ mManager.pushDynamicShortcut(si); |
| 192 | ++ } |
| 193 | ++ verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage( |
| 194 | ++ any(), any(), anyInt()); |
| 195 | ++ |
| 196 | ++ Mockito.reset(mMockUsageStatsManagerInternal); |
| 197 | ++ // Let time passes which resets the throttle |
| 198 | ++ Thread.sleep(505); |
| 199 | ++ // Verify UsageStatsManagerInternal#reportShortcutUsed can be called again |
| 200 | ++ setCaller(CALLING_PACKAGE_1, USER_0); |
| 201 | ++ mManager.pushDynamicShortcut(makeShortcut("s10")); |
| 202 | ++ setCaller(CALLING_PACKAGE_2, USER_0); |
| 203 | ++ mManager.pushDynamicShortcut(makeShortcut("s10")); |
| 204 | ++ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage( |
| 205 | ++ eq(CALLING_PACKAGE_1), any(), eq(USER_0)); |
| 206 | ++ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage( |
| 207 | ++ eq(CALLING_PACKAGE_2), any(), eq(USER_0)); |
| 208 | ++ } |
| 209 | ++ |
| 210 | + public void testUnlimitedCalls() { |
| 211 | + setCaller(CALLING_PACKAGE_1, USER_0); |
| 212 | + |
| 213 | +diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java |
| 214 | +index 86d4655e9d3a..ea134dc66e3f 100644 |
| 215 | +--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java |
| 216 | ++++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java |
| 217 | +@@ -2175,6 +2175,8 @@ public void testThrottling_resetByInternalCall() throws Exception { |
| 218 | + |
| 219 | + public void testReportShortcutUsed() { |
| 220 | + mRunningUsers.put(USER_10, true); |
| 221 | ++ mService.updateConfigurationLocked( |
| 222 | ++ ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1"); |
| 223 | + |
| 224 | + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { |
| 225 | + reset(mMockUsageStatsManagerInternal); |
0 commit comments