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

Commit b14be548 authored by Willie Koomson's avatar Willie Koomson Committed by Android (Google) Code Review
Browse files

Merge "Limit number of previews held in AppWidgetService" into main

parents 9d2ee3f3 e95aef8b
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -573,6 +573,12 @@ public final class SystemUiDeviceConfigFlags {
    public static final String GENERATED_PREVIEW_API_MAX_CALLS_PER_INTERVAL =
            "generated_preview_api_max_calls_per_interval";

    /*
     * (int) The max number of providers for which to keep generated previews.
     */
    public static final String GENERATED_PREVIEW_API_MAX_PROVIDERS =
            "generated_preview_api_max_providers";

    private SystemUiDeviceConfigFlags() {
    }
}
+38 −14
Original line number Diff line number Diff line
@@ -213,6 +213,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
            Duration.ofHours(1).toMillis();
    // Default max API calls per reset interval for generated preview API rate limiting.
    private static final int DEFAULT_GENERATED_PREVIEW_MAX_CALLS_PER_INTERVAL = 2;
    // Default max number of providers for which to keep previews.
    private static final int DEFAULT_GENERATED_PREVIEW_MAX_PROVIDERS = 50;
    // XML attribute for widget ids that are pending deletion.
    // See {@link Provider#pendingDeletedWidgetIds}.
    private static final String PENDING_DELETED_IDS_ATTR = "pending_deleted_ids";
@@ -358,10 +360,13 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
                SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_RESET_INTERVAL_MS,
                DEFAULT_GENERATED_PREVIEW_RESET_INTERVAL_MS);
        final int generatedPreviewMaxCallsPerInterval = DeviceConfig.getInt(NAMESPACE_SYSTEMUI,
                SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_RESET_INTERVAL_MS,
                SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_MAX_CALLS_PER_INTERVAL,
                DEFAULT_GENERATED_PREVIEW_MAX_CALLS_PER_INTERVAL);
        final int generatedPreviewsMaxProviders = DeviceConfig.getInt(NAMESPACE_SYSTEMUI,
                SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_MAX_PROVIDERS,
                DEFAULT_GENERATED_PREVIEW_MAX_PROVIDERS);
        mGeneratedPreviewsApiCounter = new ApiCounter(generatedPreviewResetInterval,
                generatedPreviewMaxCallsPerInterval);
                generatedPreviewMaxCallsPerInterval, generatedPreviewsMaxProviders);
        DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_SYSTEMUI,
                new HandlerExecutor(mCallbackHandler), this::handleSystemUiDeviceConfigChange);

@@ -4660,6 +4665,13 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
                        /* defaultValue= */ mGeneratedPreviewsApiCounter.getMaxCallsPerInterval());
                mGeneratedPreviewsApiCounter.setMaxCallsPerInterval(maxCallsPerInterval);
            }
            if (changed.contains(
                    SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_MAX_PROVIDERS)) {
                int maxProviders = properties.getInt(
                        SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_MAX_PROVIDERS,
                        /* defaultValue= */ mGeneratedPreviewsApiCounter.getMaxProviders());
                mGeneratedPreviewsApiCounter.setMaxProviders(maxProviders);
            }
        }
    }

@@ -5444,17 +5456,22 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        private long mResetIntervalMs;
        // The max number of API calls per interval.
        private int mMaxCallsPerInterval;
        // The max number of providers to keep call records for. Any call to tryApiCall for new
        // providers will return false after this limit.
        private int mMaxProviders;

        // Returns the current time (monotonic). By default this is SystemClock.elapsedRealtime.
        private LongSupplier mMonotonicClock;

        ApiCounter(long resetIntervalMs, int maxCallsPerInterval) {
            this(resetIntervalMs, maxCallsPerInterval, SystemClock::elapsedRealtime);
        ApiCounter(long resetIntervalMs, int maxCallsPerInterval, int maxProviders) {
            this(resetIntervalMs, maxCallsPerInterval, maxProviders, SystemClock::elapsedRealtime);
        }

        ApiCounter(long resetIntervalMs, int maxCallsPerInterval,
        ApiCounter(long resetIntervalMs, int maxCallsPerInterval, int maxProviders,
                LongSupplier monotonicClock) {
            mResetIntervalMs = resetIntervalMs;
            mMaxCallsPerInterval = maxCallsPerInterval;
            mMaxProviders = maxProviders;
            mMonotonicClock = monotonicClock;
        }

@@ -5474,12 +5491,27 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
            return mMaxCallsPerInterval;
        }

        public void setMaxProviders(int maxProviders) {
            mMaxProviders = maxProviders;
        }

        public int getMaxProviders() {
            return mMaxProviders;
        }

        /**
         * Returns true if the API call for the provider should be allowed, false if it should be
         * rate-limited.
         */
        public boolean tryApiCall(@NonNull ProviderId provider) {
            final ApiCallRecord record = getOrCreateRecord(provider);
            if (!mCallCount.containsKey(provider)) {
                if (mCallCount.size() >= mMaxProviders) {
                    return false;
                }
                mCallCount.put(provider, new ApiCallRecord());
            }
            ApiCallRecord record = mCallCount.get(provider);

            final long now = mMonotonicClock.getAsLong();
            final long timeSinceLastResetMs = now - record.lastResetTimeMs;
            // If the last reset was beyond the reset interval, reset now.
@@ -5500,14 +5532,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        public void remove(@NonNull ProviderId id) {
            mCallCount.remove(id);
        }

        @NonNull
        private ApiCallRecord getOrCreateRecord(@NonNull ProviderId provider) {
            if (!mCallCount.containsKey(provider)) {
                mCallCount.put(provider, new ApiCallRecord());
            }
            return mCallCount.get(provider);
        }
    }

    private class LoadedWidgetState {
+20 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ class ApiCounterTest {
    private companion object {
        const val RESET_INTERVAL_MS = 10L
        const val MAX_CALLS_PER_INTERVAL = 2
        const val MAX_PROVIDERS = 10
    }

    private var currentTime = 0L
@@ -34,7 +35,9 @@ class ApiCounterTest {
            /* uid= */ 123,
            ComponentName("com.android.server.appwidget", "FakeProviderClass")
        )
    private val counter = ApiCounter(RESET_INTERVAL_MS, MAX_CALLS_PER_INTERVAL) { currentTime }
    private val counter = ApiCounter(RESET_INTERVAL_MS, MAX_CALLS_PER_INTERVAL, MAX_PROVIDERS) {
        currentTime
    }

    @Test
    fun tryApiCall() {
@@ -58,4 +61,20 @@ class ApiCounterTest {
        counter.remove(id)
        assertThat(counter.tryApiCall(id)).isTrue()
    }

    @Test
    fun maxProviders() {
        for (i in 0 until MAX_PROVIDERS) {
            for (j in 0 until MAX_CALLS_PER_INTERVAL) {
                assertThat(counter.tryApiCall(providerId(i))).isTrue()
            }
        }
        assertThat(counter.tryApiCall(providerId(MAX_PROVIDERS))).isFalse()
        // remove will allow another provider to be added
        counter.remove(providerId(0))
        assertThat(counter.tryApiCall(providerId(MAX_PROVIDERS))).isTrue()
    }

    private fun providerId(i: Int) =
        AppWidgetServiceImpl.ProviderId(/* uid= */ i, id.componentName)
}