Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bd88f35c authored by Pinyao Ting's avatar Pinyao Ting
Browse files

Added throttle when reporting shortcut usage

Bug: 304290201
Test: manual
Merged-In: I96370cbd4f6a55f894c1a93307e5f82dfd394652
Change-Id: I96370cbd4f6a55f894c1a93307e5f82dfd394652
parent 925eb31c
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchResults;
import android.app.appsearch.SearchSpec;
import android.app.appsearch.SetSchemaRequest;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
@@ -47,6 +48,7 @@ import android.graphics.drawable.Icon;
import android.os.Binder;
import android.os.PersistableBundle;
import android.os.StrictMode;
import android.os.SystemClock;
import android.text.format.Formatter;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -160,6 +162,9 @@ class ShortcutPackage extends ShortcutPackageItem {
    private static final String KEY_BITMAPS = "bitmaps";
    private static final String KEY_BITMAP_BYTES = "bitmapBytes";

    @VisibleForTesting
    public static final int REPORT_USAGE_BUFFER_SIZE = 3;

    private final Executor mExecutor;

    /**
@@ -195,6 +200,9 @@ class ShortcutPackage extends ShortcutPackageItem {

    private long mLastKnownForegroundElapsedTime;

    @GuardedBy("mLock")
    private List<Long> mLastReportedTime = new ArrayList<>();

    @GuardedBy("mLock")
    private boolean mIsAppSearchSchemaUpToDate;

@@ -1673,6 +1681,30 @@ class ShortcutPackage extends ShortcutPackageItem {
        return condition[0];
    }

    void reportShortcutUsed(@NonNull final UsageStatsManagerInternal usageStatsManagerInternal,
            @NonNull final String shortcutId) {
        synchronized (mLock) {
            final long currentTS = SystemClock.elapsedRealtime();
            final ShortcutService s = mShortcutUser.mService;
            if (mLastReportedTime.isEmpty()
                    || mLastReportedTime.size() < REPORT_USAGE_BUFFER_SIZE) {
                mLastReportedTime.add(currentTS);
            } else if (currentTS - mLastReportedTime.get(0) > s.mSaveDelayMillis) {
                mLastReportedTime.remove(0);
                mLastReportedTime.add(currentTS);
            } else {
                return;
            }
            final long token = s.injectClearCallingIdentity();
            try {
                usageStatsManagerInternal.reportShortcutUsage(getPackageName(), shortcutId,
                        getUser().getUserId());
            } finally {
                s.injectRestoreCallingIdentity(token);
            }
        }
    }

    public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) {
        pw.println();

+5 −13
Original line number Diff line number Diff line
@@ -364,7 +364,7 @@ public class ShortcutService extends IShortcutService.Stub {
    private CompressFormat mIconPersistFormat;
    private int mIconPersistQuality;

    private int mSaveDelayMillis;
    int mSaveDelayMillis;

    private final IPackageManager mIPackageManager;
    private final PackageManagerInternal mPackageManagerInternal;
@@ -2268,7 +2268,7 @@ public class ShortcutService extends IShortcutService.Stub {

        packageShortcutsChanged(ps, changedShortcuts, removedShortcuts);

        reportShortcutUsedInternal(packageName, shortcut.getId(), userId);
        ps.reportShortcutUsed(mUsageStatsManagerInternal, shortcut.getId());

        verifyStates();
    }
@@ -2669,25 +2669,17 @@ public class ShortcutService extends IShortcutService.Stub {
            Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d",
                    shortcutId, packageName, userId));
        }
        final ShortcutPackage ps;
        synchronized (mLock) {
            throwIfUserLockedL(userId);
            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
            ps = getPackageShortcutsForPublisherLocked(packageName, userId);
            if (ps.findShortcutById(shortcutId) == null) {
                Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s",
                        packageName, shortcutId));
                return;
            }
        }
        reportShortcutUsedInternal(packageName, shortcutId, userId);
    }

    private void reportShortcutUsedInternal(String packageName, String shortcutId, int userId) {
        final long token = injectClearCallingIdentity();
        try {
            mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId);
        } finally {
            injectRestoreCallingIdentity(token);
        }
        ps.reportShortcutUsed(mUsageStatsManagerInternal, shortcutId);
    }

    @Override
+53 −2
Original line number Diff line number Diff line
@@ -402,8 +402,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {

    public void testPushDynamicShortcut() {
        // Change the max number of shortcuts.
        mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5");

        mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5,"
                + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
        setCaller(CALLING_PACKAGE_1, USER_0);

        final ShortcutInfo s1 = makeShortcut("s1");
@@ -541,6 +541,57 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
                eq(CALLING_PACKAGE_1), eq("s9"), eq(USER_0));
    }

    public void testPushDynamicShortcut_CallsToUsageStatsManagerAreThrottled()
            throws InterruptedException {
        mService.updateConfigurationLocked(
                ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=500");

        // Verify calls to UsageStatsManagerInternal#reportShortcutUsage are throttled.
        setCaller(CALLING_PACKAGE_1, USER_0);
        for (int i = 0; i < ShortcutPackage.REPORT_USAGE_BUFFER_SIZE; i++) {
            final ShortcutInfo si = makeShortcut("s" + i);
            mManager.pushDynamicShortcut(si);
        }
        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
                eq(CALLING_PACKAGE_1), eq("s1"), eq(USER_0));
        Mockito.reset(mMockUsageStatsManagerInternal);
        for (int i = ShortcutPackage.REPORT_USAGE_BUFFER_SIZE; i <= 10; i++) {
            final ShortcutInfo si = makeShortcut("s" + i);
            mManager.pushDynamicShortcut(si);
        }
        verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
                any(), any(), anyInt());

        // Verify pkg2 isn't blocked by pkg1, but consecutive calls from pkg2 are throttled as well.
        setCaller(CALLING_PACKAGE_2, USER_0);
        for (int i = 0; i < ShortcutPackage.REPORT_USAGE_BUFFER_SIZE; i++) {
            final ShortcutInfo si = makeShortcut("s" + i);
            mManager.pushDynamicShortcut(si);
        }
        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
                eq(CALLING_PACKAGE_2), eq("s1"), eq(USER_0));
        Mockito.reset(mMockUsageStatsManagerInternal);
        for (int i = ShortcutPackage.REPORT_USAGE_BUFFER_SIZE; i <= 10; i++) {
            final ShortcutInfo si = makeShortcut("s" + i);
            mManager.pushDynamicShortcut(si);
        }
        verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
                any(), any(), anyInt());

        Mockito.reset(mMockUsageStatsManagerInternal);
        // Let time passes which resets the throttle
        Thread.sleep(505);
        // Verify UsageStatsManagerInternal#reportShortcutUsed can be called again
        setCaller(CALLING_PACKAGE_1, USER_0);
        mManager.pushDynamicShortcut(makeShortcut("s10"));
        setCaller(CALLING_PACKAGE_2, USER_0);
        mManager.pushDynamicShortcut(makeShortcut("s10"));
        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
                eq(CALLING_PACKAGE_1), any(), eq(USER_0));
        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
                eq(CALLING_PACKAGE_2), any(), eq(USER_0));
    }

    public void testUnlimitedCalls() {
        setCaller(CALLING_PACKAGE_1, USER_0);

+2 −0
Original line number Diff line number Diff line
@@ -2175,6 +2175,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {

    public void testReportShortcutUsed() {
        mRunningUsers.put(USER_10, true);
        mService.updateConfigurationLocked(
                ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");

        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
            reset(mMockUsageStatsManagerInternal);