diff options
| author | 2021-04-19 11:13:13 -0700 | |
|---|---|---|
| committer | 2021-05-26 01:08:50 +0000 | |
| commit | 951f75637fd717aa1ca8b8709df3044b011dc08c (patch) | |
| tree | 57bcaba0be972bdc36cd5788f5be2ce06fb1c11f | |
| parent | 89b92e20ebd0588c02607b14d88b8746233a3ff9 (diff) | |
Add AppSearchConfig in AppSearch
It manages the P/H flags from DeviceConfig.
bug: 173532925
Test: atest FrameworksMockingServicesTests:AppSearchConfigManagerTest
Change-Id: I668ecd8190bbe7a21ab3a23b52d08f4dce400491
5 files changed, 623 insertions, 84 deletions
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java new file mode 100644 index 000000000000..6e81afcdc912 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2021 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.appsearch; + +import android.annotation.NonNull; +import android.os.Bundle; +import android.provider.DeviceConfig; +import android.provider.DeviceConfig.OnPropertiesChangedListener; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * It contains all the keys for the flags, as well as caches some of latest flag values from + * DeviceConfig. + * + * <p>Though the latest flag values can always be retrieved by calling {@code + * DeviceConfig.getProperty}, we want to cache some of those values. For example, the sampling + * intervals for logging, they are needed for each api call and it would be a little expensive to + * call + * {@code DeviceConfig.getProperty} every time. + * + * <p>Listener is registered to DeviceConfig keep the cached value up to date. + * + * <p>This class is thread-safe. + * + * @hide + */ +public final class AppSearchConfig implements AutoCloseable { + /** + * It would be used as default min time interval between samples in millis if there is no value + * set for {@link AppSearchConfig#KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS} in DeviceConfig. + */ + @VisibleForTesting + static final long DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 50; + + /** + * It would be used as default sampling interval if there is no value + * set for {@link AppSearchConfig#KEY_SAMPLING_INTERVAL_DEFAULT} in DeviceConfig. + */ + @VisibleForTesting + static final int DEFAULT_SAMPLING_INTERVAL = 10; + + /* + * Keys for ALL the flags stored in DeviceConfig. + */ + public static final String KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = + "min_time_interval_between_samples_millis"; + public static final String KEY_SAMPLING_INTERVAL_DEFAULT = "sampling_interval_default"; + public static final String KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS = + "sampling_interval_for_batch_call_stats"; + public static final String KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS = + "sampling_interval_for_put_document_stats"; + + // Array contains all the corresponding keys for the cached values. + private static final String[] KEYS_TO_ALL_CACHED_VALUES = { + KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, + KEY_SAMPLING_INTERVAL_DEFAULT, + KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, + KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS + }; + + // Lock needed for all the operations in this class. + private final Object mLock = new Object(); + + /** + * Bundle to hold all the cached flag values corresponding to + * {@link AppSearchConfig#KEYS_TO_ALL_CACHED_VALUES}. + */ + @GuardedBy("mLock") + private final Bundle mBundleLocked = new Bundle(); + + + @GuardedBy("mLock") + private boolean mIsClosedLocked = false; + + /** Listener to update cached flag values from DeviceConfig. */ + private final OnPropertiesChangedListener mOnDeviceConfigChangedListener = + properties -> { + if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_APPSEARCH)) { + return; + } + + updateCachedValues(properties); + }; + + /** + * Creates an instance of {@link AppSearchConfig}. + * + * @param executor used to fetch and cache the flag values from DeviceConfig during creation or + * config change. + */ + @NonNull + public static AppSearchConfig create(@NonNull Executor executor) { + Objects.requireNonNull(executor); + AppSearchConfig configManager = new AppSearchConfig(); + configManager.initialize(executor); + return configManager; + } + + private AppSearchConfig() { + } + + /** + * Initializes the {@link AppSearchConfig} + * + * <p>It fetches the custom properties from DeviceConfig if available. + * + * @param executor listener would be run on to handle P/H flag change. + */ + private void initialize(@NonNull Executor executor) { + executor.execute(() -> { + // Attach the callback to get updates on those properties. + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APPSEARCH, + executor, + mOnDeviceConfigChangedListener); + + DeviceConfig.Properties properties = DeviceConfig.getProperties( + DeviceConfig.NAMESPACE_APPSEARCH, KEYS_TO_ALL_CACHED_VALUES); + updateCachedValues(properties); + }); + } + + // TODO(b/173532925) check this will be called. If we have a singleton instance for this + // class, probably we don't need it. + @Override + public void close() { + synchronized (mLock) { + if (mIsClosedLocked) { + return; + } + + DeviceConfig.removeOnPropertiesChangedListener(mOnDeviceConfigChangedListener); + mIsClosedLocked = true; + } + } + + /** Returns cached value for minTimeIntervalBetweenSamplesMillis. */ + public long getCachedMinTimeIntervalBetweenSamplesMillis() { + synchronized (mLock) { + throwIfClosedLocked(); + return mBundleLocked.getLong(KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, + DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS); + } + } + + /** + * Returns cached value for default sampling interval for all the stats NOT listed in + * the configuration. + * + * <p>For example, sampling_interval=10 means that one out of every 10 stats was logged. + */ + public int getCachedSamplingIntervalDefault() { + synchronized (mLock) { + throwIfClosedLocked(); + return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_DEFAULT, DEFAULT_SAMPLING_INTERVAL); + } + } + + /** + * Returns cached value for sampling interval for batch calls. + * + * <p>For example, sampling_interval=10 means that one out of every 10 stats was logged. + */ + public int getCachedSamplingIntervalForBatchCallStats() { + synchronized (mLock) { + throwIfClosedLocked(); + return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, + getCachedSamplingIntervalDefault()); + } + } + + /** + * Returns cached value for sampling interval for putDocument. + * + * <p>For example, sampling_interval=10 means that one out of every 10 stats was logged. + */ + public int getCachedSamplingIntervalForPutDocumentStats() { + synchronized (mLock) { + throwIfClosedLocked(); + return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, + getCachedSamplingIntervalDefault()); + } + } + + @GuardedBy("mLock") + private void throwIfClosedLocked() { + if (mIsClosedLocked) { + throw new IllegalStateException("Trying to use a closed AppSearchConfig instance."); + } + } + + private void updateCachedValues(@NonNull DeviceConfig.Properties properties) { + for (String key : properties.getKeyset()) { + updateCachedValue(key, properties); + } + } + + private void updateCachedValue(@NonNull String key, + @NonNull DeviceConfig.Properties properties) { + if (properties.getString(key, /*defaultValue=*/ null) == null) { + // Key is missing or value is just null. That is not expected if the key is + // defined in the configuration. + // + // We choose NOT to put the default value in the bundle. + // Instead, we let the getters handle what default value should be returned. + // + // Also we keep the old value in the bundle. So getters can still + // return last valid value. + return; + } + + switch (key) { + case KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS: + synchronized (mLock) { + mBundleLocked.putLong(key, + properties.getLong(key, + DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS)); + } + break; + case KEY_SAMPLING_INTERVAL_DEFAULT: + case KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS: + case KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS: + synchronized (mLock) { + mBundleLocked.putInt(key, properties.getInt(key, DEFAULT_SAMPLING_INTERVAL)); + } + break; + default: + break; + } + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java index abcf1611ab04..7d0ce415502b 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java @@ -72,7 +72,7 @@ public final class PlatformLogger implements AppSearchLogger { * * <p> We can have correct extrapolated number by adding those counts back when we log * the same type of stats next time. E.g. the true count of an event could be estimated as: - * SUM(sampling_ratio * (num_skipped_sample + 1)) as est_count + * SUM(sampling_interval * (num_skipped_sample + 1)) as est_count * * <p>The key to the SparseArray is {@link CallStats.CallType} */ @@ -105,42 +105,42 @@ public final class PlatformLogger implements AppSearchLogger { // logging again. private final long mMinTimeIntervalBetweenSamplesMillis; - // Default sampling ratio for all types of stats - private final int mDefaultSamplingRatio; + // Default sampling interval for all types of stats + private final int mDefaultSamplingInterval; /** - * Sampling ratios for different types of stats + * Sampling intervals for different types of stats * * <p>This SparseArray is passed by client and is READ-ONLY. The key to that SparseArray is * {@link CallStats.CallType} * - * <p>If sampling ratio is missing for certain stats type, - * {@link Config#mDefaultSamplingRatio} will be used. + * <p>If sampling interval is missing for certain stats type, + * {@link Config#mDefaultSamplingInterval} will be used. * - * <p>E.g. sampling ratio=10 means that one out of every 10 stats was logged. If sampling - * ratio is 1, we will log each sample and it acts as if the sampling is disabled. + * <p>E.g. sampling interval=10 means that one out of every 10 stats was logged. If sampling + * interval is 1, we will log each sample and it acts as if the sampling is disabled. */ @NonNull - private final SparseIntArray mSamplingRatios; + private final SparseIntArray mSamplingIntervals; /** * Configuration for {@link PlatformLogger} * * @param minTimeIntervalBetweenSamplesMillis minimum time interval apart in Milliseconds * required for two consecutive stats logged - * @param defaultSamplingRatio default sampling ratio - * @param samplingRatios SparseArray to customize sampling ratio for + * @param defaultSamplingInterval default sampling interval + * @param samplingIntervals SparseArray to customize sampling interval for * different stat types */ public Config(long minTimeIntervalBetweenSamplesMillis, - int defaultSamplingRatio, - @NonNull SparseIntArray samplingRatios) { + int defaultSamplingInterval, + @NonNull SparseIntArray samplingIntervals) { // TODO(b/173532925) Probably we can get rid of those three after we have p/h flags // for them. - // e.g. we can just call DeviceConfig.get(SAMPLING_RATIO_FOR_PUT_DOCUMENTS). + // e.g. we can just call DeviceConfig.get(SAMPLING_INTERVAL_FOR_PUT_DOCUMENTS). mMinTimeIntervalBetweenSamplesMillis = minTimeIntervalBetweenSamplesMillis; - mDefaultSamplingRatio = defaultSamplingRatio; - mSamplingRatios = samplingRatios; + mDefaultSamplingInterval = defaultSamplingInterval; + mSamplingIntervals = samplingIntervals; } } @@ -150,14 +150,14 @@ public final class PlatformLogger implements AppSearchLogger { static final class ExtraStats { // UID for the calling package of the stats. final int mPackageUid; - // sampling ratio for the call type of the stats. - final int mSamplingRatio; + // sampling interval for the call type of the stats. + final int mSamplingInterval; // number of samplings skipped before the current one for the same call type. final int mSkippedSampleCount; - ExtraStats(int packageUid, int samplingRatio, int skippedSampleCount) { + ExtraStats(int packageUid, int samplingInterval, int skippedSampleCount) { mPackageUid = packageUid; - mSamplingRatio = samplingRatio; + mSamplingInterval = samplingInterval; mSkippedSampleCount = skippedSampleCount; } } @@ -224,7 +224,7 @@ public final class PlatformLogger implements AppSearchLogger { * * @return removed UID for the package, or {@code INVALID_UID} if package was not previously * cached. - */ + */ public int removeCachedUidForPackage(@NonNull String packageName) { // TODO(b/173532925) This needs to be called when we get PACKAGE_REMOVED intent Objects.requireNonNull(packageName); @@ -243,7 +243,7 @@ public final class PlatformLogger implements AppSearchLogger { try { int hashCodeForDatabase = calculateHashCodeMd5(database); AppSearchStatsLog.write(AppSearchStatsLog.APP_SEARCH_CALL_STATS_REPORTED, - extraStats.mSamplingRatio, + extraStats.mSamplingInterval, extraStats.mSkippedSampleCount, extraStats.mPackageUid, hashCodeForDatabase, @@ -275,7 +275,7 @@ public final class PlatformLogger implements AppSearchLogger { try { int hashCodeForDatabase = calculateHashCodeMd5(database); AppSearchStatsLog.write(AppSearchStatsLog.APP_SEARCH_PUT_DOCUMENT_STATS_REPORTED, - extraStats.mSamplingRatio, + extraStats.mSamplingInterval, extraStats.mSkippedSampleCount, extraStats.mPackageUid, hashCodeForDatabase, @@ -312,7 +312,7 @@ public final class PlatformLogger implements AppSearchLogger { try { int hashCodeForDatabase = calculateHashCodeMd5(database); AppSearchStatsLog.write(AppSearchStatsLog.APP_SEARCH_QUERY_STATS_REPORTED, - extraStats.mSamplingRatio, + extraStats.mSamplingInterval, extraStats.mSkippedSampleCount, extraStats.mPackageUid, hashCodeForDatabase, @@ -355,7 +355,7 @@ public final class PlatformLogger implements AppSearchLogger { ExtraStats extraStats = createExtraStatsLocked(/*packageName=*/ null, CallStats.CALL_TYPE_INITIALIZE); AppSearchStatsLog.write(AppSearchStatsLog.APP_SEARCH_INITIALIZE_STATS_REPORTED, - extraStats.mSamplingRatio, + extraStats.mSamplingInterval, extraStats.mSkippedSampleCount, extraStats.mPackageUid, stats.getStatusCode(), @@ -428,14 +428,14 @@ public final class PlatformLogger implements AppSearchLogger { packageUid = getPackageUidAsUserLocked(packageName); } - int samplingRatio = mConfig.mSamplingRatios.get(callType, - mConfig.mDefaultSamplingRatio); + int samplingInterval = mConfig.mSamplingIntervals.get(callType, + mConfig.mDefaultSamplingInterval); int skippedSampleCount = mSkippedSampleCountLocked.get(callType, /*valueOfKeyIfNotFound=*/ 0); mSkippedSampleCountLocked.put(callType, 0); - return new ExtraStats(packageUid, samplingRatio, skippedSampleCount); + return new ExtraStats(packageUid, samplingInterval, skippedSampleCount); } /** @@ -450,11 +450,11 @@ public final class PlatformLogger implements AppSearchLogger { // rate limiting. @VisibleForTesting boolean shouldLogForTypeLocked(@CallStats.CallType int callType) { - int samplingRatio = mConfig.mSamplingRatios.get(callType, - mConfig.mDefaultSamplingRatio); + int samplingInterval = mConfig.mSamplingIntervals.get(callType, + mConfig.mDefaultSamplingInterval); // Sampling - if (!shouldSample(samplingRatio)) { + if (!shouldSample(samplingInterval)) { return false; } @@ -475,15 +475,15 @@ public final class PlatformLogger implements AppSearchLogger { /** * Checks if the stats should be "sampled" * - * @param samplingRatio sampling ratio + * @param samplingInterval sampling interval * @return if the stats should be sampled */ - private boolean shouldSample(int samplingRatio) { - if (samplingRatio <= 0) { + private boolean shouldSample(int samplingInterval) { + if (samplingInterval <= 0) { return false; } - return mRng.nextInt((int) samplingRatio) == 0; + return mRng.nextInt((int) samplingInterval) == 0; } /** diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 64b9a63991b4..24e11af23798 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -31,7 +31,10 @@ android_test { name: "FrameworksMockingServicesTests", defaults: ["FrameworkMockingServicesTests-jni-defaults"], - srcs: ["src/**/*.java", "src/**/*.kt"], + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], static_libs: [ "services.core", @@ -41,6 +44,7 @@ android_test { "service-jobscheduler", "service-permission.impl", "service-blobstore", + "service-appsearch", "androidx.test.runner", "androidx.test.ext.truth", "mockito-target-extended-minus-junit4", diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java new file mode 100644 index 000000000000..c4860287c567 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2021 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.appsearch; + +import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; + +import static com.google.common.truth.Truth.assertThat; + +import android.provider.DeviceConfig; + +import com.android.server.testables.TestableDeviceConfig; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; + +/** + * Tests for {@link AppSearchConfig}. + * + * <p>Build/Install/Run: atest FrameworksMockingServicesTests:AppSearchConfigTest + */ +public class AppSearchConfigTest { + @Rule + public final TestableDeviceConfig.TestableDeviceConfigRule + mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); + + @Test + public void testDefaultValues_allCachedValue() { + AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); + + assertThat(appSearchConfig.getCachedMinTimeIntervalBetweenSamplesMillis()).isEqualTo( + AppSearchConfig.DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS); + assertThat(appSearchConfig.getCachedSamplingIntervalDefault()).isEqualTo( + AppSearchConfig.DEFAULT_SAMPLING_INTERVAL); + assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( + AppSearchConfig.DEFAULT_SAMPLING_INTERVAL); + assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo( + AppSearchConfig.DEFAULT_SAMPLING_INTERVAL); + } + + @Test + public void testCustomizedValue_minTimeIntervalBetweenSamplesMillis() { + final long minTimeIntervalBetweenSamplesMillis = -1; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, + Long.toString(minTimeIntervalBetweenSamplesMillis), + false); + + AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); + + assertThat(appSearchConfig.getCachedMinTimeIntervalBetweenSamplesMillis()).isEqualTo( + minTimeIntervalBetweenSamplesMillis); + } + + @Test + public void testCustomizedValueOverride_minTimeIntervalBetweenSamplesMillis() { + long minTimeIntervalBetweenSamplesMillis = -1; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, + Long.toString(minTimeIntervalBetweenSamplesMillis), + false); + AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); + + minTimeIntervalBetweenSamplesMillis = -2; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, + Long.toString(minTimeIntervalBetweenSamplesMillis), + false); + + assertThat(appSearchConfig.getCachedMinTimeIntervalBetweenSamplesMillis()).isEqualTo( + minTimeIntervalBetweenSamplesMillis); + } + + @Test + public void testCustomizedValue_allSamplingIntervals() { + final int samplingIntervalDefault = -1; + final int samplingIntervalPutDocumentStats = -2; + final int samplingIntervalBatchCallStats = -3; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Integer.toString(samplingIntervalDefault), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, + Integer.toString(samplingIntervalPutDocumentStats), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, + Integer.toString(samplingIntervalBatchCallStats), + false); + + AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); + + assertThat(appSearchConfig.getCachedSamplingIntervalDefault()).isEqualTo( + samplingIntervalDefault); + assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo( + samplingIntervalPutDocumentStats); + assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( + samplingIntervalBatchCallStats); + } + + @Test + public void testCustomizedValueOverride_allSamplingIntervals() { + int samplingIntervalDefault = -1; + int samplingIntervalPutDocumentStats = -2; + int samplingIntervalBatchCallStats = -3; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Integer.toString(samplingIntervalDefault), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, + Integer.toString(samplingIntervalPutDocumentStats), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, + Integer.toString(samplingIntervalBatchCallStats), + false); + AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); + + // Overrides + samplingIntervalDefault = -4; + samplingIntervalPutDocumentStats = -5; + samplingIntervalBatchCallStats = -6; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Integer.toString(samplingIntervalDefault), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, + Integer.toString(samplingIntervalPutDocumentStats), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, + Integer.toString(samplingIntervalBatchCallStats), + false); + + assertThat(appSearchConfig.getCachedSamplingIntervalDefault()).isEqualTo( + samplingIntervalDefault); + assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo( + samplingIntervalPutDocumentStats); + assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( + samplingIntervalBatchCallStats); + } + + /** + * Tests if we fall back to {@link AppSearchConfig#DEFAULT_SAMPLING_INTERVAL} if both default + * sampling + * interval and custom value are not set in DeviceConfig, and there is some other sampling + * interval + * set. + */ + @Test + public void testFallbackToDefaultSamplingValue_useHardCodedDefault() { + final int samplingIntervalPutDocumentStats = -1; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, + Integer.toString(samplingIntervalPutDocumentStats), + false); + + AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); + + assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo( + samplingIntervalPutDocumentStats); + assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( + AppSearchConfig.DEFAULT_SAMPLING_INTERVAL); + } + + // Tests if we fall back to configured default sampling interval if custom value is not set in + // DeviceConfig. + @Test + public void testFallbackDefaultSamplingValue_useConfiguredDefault() { + final int samplingIntervalPutDocumentStats = -1; + final int samplingIntervalDefault = -2; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, + Integer.toString(samplingIntervalPutDocumentStats), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Integer.toString(samplingIntervalDefault), + false); + + AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); + + assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo( + samplingIntervalPutDocumentStats); + assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( + samplingIntervalDefault); + } + + // Tests that cached values should reflect latest values in DeviceConfig. + @Test + public void testFallbackDefaultSamplingValue_defaultValueChanged() { + int samplingIntervalPutDocumentStats = -1; + int samplingIntervalDefault = -2; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, + Integer.toString(samplingIntervalPutDocumentStats), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Integer.toString(samplingIntervalDefault), + false); + + AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); + + // Sampling values changed. + samplingIntervalPutDocumentStats = -3; + samplingIntervalDefault = -4; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, + Integer.toString(samplingIntervalPutDocumentStats), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Integer.toString(samplingIntervalDefault), + false); + + assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo( + samplingIntervalPutDocumentStats); + assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( + samplingIntervalDefault); + } + + // Tests default sampling interval won't affect custom sampling intervals if they are set. + @Test + public void testShouldNotFallBack_ifValueConfigured() { + int samplingIntervalDefault = -1; + int samplingIntervalBatchCallStats = -2; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Integer.toString(samplingIntervalDefault), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, + Integer.toString(samplingIntervalBatchCallStats), + false); + + AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); + + // Default sampling interval changed. + samplingIntervalDefault = -3; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Integer.toString(samplingIntervalDefault), + false); + + assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( + samplingIntervalBatchCallStats); + } + + @Test + public void testNotUsable_afterClose() { + AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); + + appSearchConfig.close(); + + Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", + IllegalStateException.class, + () -> appSearchConfig.getCachedMinTimeIntervalBetweenSamplesMillis()); + Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", + IllegalStateException.class, + () -> appSearchConfig.getCachedSamplingIntervalDefault()); + Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", + IllegalStateException.class, + () -> appSearchConfig.getCachedSamplingIntervalForBatchCallStats()); + Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", + IllegalStateException.class, + () -> appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java index 747dd1df1869..734f05a8de91 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java @@ -46,7 +46,7 @@ import java.security.NoSuchAlgorithmException; public class PlatformLoggerTest { private static final int TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 100; - private static final int TEST_DEFAULT_SAMPLING_RATIO = 10; + private static final int TEST_DEFAULT_SAMPLING_INTERVAL = 10; private static final String TEST_PACKAGE_NAME = "packageName"; private MockPackageManager mMockPackageManager = new MockPackageManager(); private Context mContext; @@ -72,63 +72,63 @@ public class PlatformLoggerTest { } @Test - public void testCreateExtraStatsLocked_nullSamplingRatioMap_returnsDefaultSamplingRatio() { + public void testCreateExtraStatsLocked_nullSamplingIntervalMap_returnsDefault() { PlatformLogger logger = new PlatformLogger( ApplicationProvider.getApplicationContext(), UserHandle.of(UserHandle.USER_NULL), new PlatformLogger.Config( TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - TEST_DEFAULT_SAMPLING_RATIO, - /*samplingRatios=*/ new SparseIntArray())); + TEST_DEFAULT_SAMPLING_INTERVAL, + /*samplingIntervals=*/ new SparseIntArray())); - // Make sure default sampling ratio is used if samplingMap is not provided. + // Make sure default sampling interval is used if samplingMap is not provided. assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_UNKNOWN).mSamplingRatio).isEqualTo( - TEST_DEFAULT_SAMPLING_RATIO); + CallStats.CALL_TYPE_UNKNOWN).mSamplingInterval).isEqualTo( + TEST_DEFAULT_SAMPLING_INTERVAL); assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_INITIALIZE).mSamplingRatio).isEqualTo( - TEST_DEFAULT_SAMPLING_RATIO); + CallStats.CALL_TYPE_INITIALIZE).mSamplingInterval).isEqualTo( + TEST_DEFAULT_SAMPLING_INTERVAL); assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_SEARCH).mSamplingRatio).isEqualTo( - TEST_DEFAULT_SAMPLING_RATIO); + CallStats.CALL_TYPE_SEARCH).mSamplingInterval).isEqualTo( + TEST_DEFAULT_SAMPLING_INTERVAL); assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_FLUSH).mSamplingRatio).isEqualTo( - TEST_DEFAULT_SAMPLING_RATIO); + CallStats.CALL_TYPE_FLUSH).mSamplingInterval).isEqualTo( + TEST_DEFAULT_SAMPLING_INTERVAL); } @Test - public void testCreateExtraStatsLocked_with_samplingRatioMap_returnsConfiguredSamplingRatio() { - int putDocumentSamplingRatio = 1; - int querySamplingRatio = 2; - final SparseIntArray samplingRatios = new SparseIntArray(); - samplingRatios.put(CallStats.CALL_TYPE_PUT_DOCUMENT, putDocumentSamplingRatio); - samplingRatios.put(CallStats.CALL_TYPE_SEARCH, querySamplingRatio); + public void testCreateExtraStatsLocked_with_samplingIntervalMap_returnsConfigured() { + int putDocumentSamplingInterval = 1; + int querySamplingInterval = 2; + final SparseIntArray samplingIntervals = new SparseIntArray(); + samplingIntervals.put(CallStats.CALL_TYPE_PUT_DOCUMENT, putDocumentSamplingInterval); + samplingIntervals.put(CallStats.CALL_TYPE_SEARCH, querySamplingInterval); PlatformLogger logger = new PlatformLogger( ApplicationProvider.getApplicationContext(), UserHandle.of(UserHandle.USER_NULL), new PlatformLogger.Config( TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - TEST_DEFAULT_SAMPLING_RATIO, - samplingRatios)); + TEST_DEFAULT_SAMPLING_INTERVAL, + samplingIntervals)); - // The default sampling ratio should be used if no sampling ratio is + // The default sampling interval should be used if no sampling interval is // provided for certain call type. assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_INITIALIZE).mSamplingRatio).isEqualTo( - TEST_DEFAULT_SAMPLING_RATIO); + CallStats.CALL_TYPE_INITIALIZE).mSamplingInterval).isEqualTo( + TEST_DEFAULT_SAMPLING_INTERVAL); assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_FLUSH).mSamplingRatio).isEqualTo( - TEST_DEFAULT_SAMPLING_RATIO); + CallStats.CALL_TYPE_FLUSH).mSamplingInterval).isEqualTo( + TEST_DEFAULT_SAMPLING_INTERVAL); - // The configured sampling ratio is used if sampling ratio is available + // The configured sampling interval is used if sampling interval is available // for certain call type. assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_PUT_DOCUMENT).mSamplingRatio).isEqualTo( - putDocumentSamplingRatio); + CallStats.CALL_TYPE_PUT_DOCUMENT).mSamplingInterval).isEqualTo( + putDocumentSamplingInterval); assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_SEARCH).mSamplingRatio).isEqualTo( - querySamplingRatio); + CallStats.CALL_TYPE_SEARCH).mSamplingInterval).isEqualTo( + querySamplingInterval); } @Test @@ -202,16 +202,16 @@ public class PlatformLoggerTest { } @Test - public void testShouldLogForTypeLocked_trueWhenSampleRatioIsOne() { - final int samplingRatio = 1; + public void testShouldLogForTypeLocked_trueWhenSampleIntervalIsOne() { + final int samplingInterval = 1; final String testPackageName = "packageName"; PlatformLogger logger = new PlatformLogger( ApplicationProvider.getApplicationContext(), UserHandle.of(UserHandle.USER_NULL), new PlatformLogger.Config( TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - samplingRatio, - /*samplingRatios=*/ new SparseIntArray())); + samplingInterval, + /*samplingIntervals=*/ new SparseIntArray())); // Sample should always be logged for the first time if sampling is disabled(value is one). assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue(); @@ -220,18 +220,18 @@ public class PlatformLoggerTest { } @Test - public void testShouldLogForTypeLocked_falseWhenSampleRatioIsNegative() { - final int samplingRatio = -1; + public void testShouldLogForTypeLocked_falseWhenSampleIntervalIsNegative() { + final int samplingInterval = -1; final String testPackageName = "packageName"; PlatformLogger logger = new PlatformLogger( ApplicationProvider.getApplicationContext(), UserHandle.of(UserHandle.USER_NULL), new PlatformLogger.Config( TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - samplingRatio, - /*samplingRatios=*/ new SparseIntArray())); + samplingInterval, + /*samplingIntervals=*/ new SparseIntArray())); - // Makes sure sample will be excluded due to sampling if sample ratio is negative. + // Makes sure sample will be excluded due to sampling if sample interval is negative. assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse(); // Skipped count should be 0 since it doesn't pass the sampling. assertThat(logger.createExtraStatsLocked(testPackageName, @@ -241,7 +241,7 @@ public class PlatformLoggerTest { @Test public void testShouldLogForTypeLocked_falseWhenWithinCoolOffInterval() { // Next sample won't be excluded due to sampling. - final int samplingRatio = 1; + final int samplingInterval = 1; // Next sample would guaranteed to be too close. final int minTimeIntervalBetweenSamplesMillis = Integer.MAX_VALUE; final String testPackageName = "packageName"; @@ -250,8 +250,8 @@ public class PlatformLoggerTest { UserHandle.of(UserHandle.USER_NULL), new PlatformLogger.Config( minTimeIntervalBetweenSamplesMillis, - samplingRatio, - /*samplingRatios=*/ new SparseIntArray())); + samplingInterval, + /*samplingIntervals=*/ new SparseIntArray())); logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime()); // Makes sure sample will be excluded due to rate limiting if samples are too close. @@ -263,7 +263,7 @@ public class PlatformLoggerTest { @Test public void testShouldLogForTypeLocked_trueWhenOutsideOfCoolOffInterval() { // Next sample won't be excluded due to sampling. - final int samplingRatio = 1; + final int samplingInterval = 1; // Next sample would guaranteed to be included. final int minTimeIntervalBetweenSamplesMillis = 0; final String testPackageName = "packageName"; @@ -272,8 +272,8 @@ public class PlatformLoggerTest { UserHandle.of(UserHandle.USER_NULL), new PlatformLogger.Config( minTimeIntervalBetweenSamplesMillis, - samplingRatio, - /*samplingRatios=*/ new SparseIntArray())); + samplingInterval, + /*samplingIntervals=*/ new SparseIntArray())); logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime()); // Makes sure sample will be logged if it is not too close to previous sample. @@ -292,8 +292,8 @@ public class PlatformLoggerTest { mContext.getUser(), new PlatformLogger.Config( TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - TEST_DEFAULT_SAMPLING_RATIO, - /*samplingRatios=*/ new SparseIntArray())); + TEST_DEFAULT_SAMPLING_INTERVAL, + /*samplingIntervals=*/ new SparseIntArray())); mMockPackageManager.mockGetPackageUidAsUser(testPackageName, mContext.getUserId(), testUid); // |