diff options
7 files changed, 325 insertions, 231 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 index 6e81afcdc912..d5271a6cb92e 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java @@ -44,6 +44,8 @@ import java.util.concurrent.Executor; * @hide */ public final class AppSearchConfig implements AutoCloseable { + private static volatile AppSearchConfig sConfig; + /** * 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. @@ -101,12 +103,16 @@ public final class AppSearchConfig implements AutoCloseable { updateCachedValues(properties); }; + private AppSearchConfig() { + } + /** * Creates an instance of {@link AppSearchConfig}. * * @param executor used to fetch and cache the flag values from DeviceConfig during creation or * config change. */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) @NonNull public static AppSearchConfig create(@NonNull Executor executor) { Objects.requireNonNull(executor); @@ -115,7 +121,23 @@ public final class AppSearchConfig implements AutoCloseable { return configManager; } - private AppSearchConfig() { + /** + * Gets an instance of {@link AppSearchConfig} to be used. + * + * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the + * existing instance will be returned. + */ + @NonNull + public static AppSearchConfig getInstance(@NonNull Executor executor) { + Objects.requireNonNull(executor); + if (sConfig == null) { + synchronized (AppSearchConfig.class) { + if (sConfig == null) { + sConfig = create(executor); + } + } + } + return sConfig; } /** diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index 0709ff5fc48c..a3b89b0f27fb 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -224,7 +224,7 @@ public class AppSearchManagerService extends SystemService { if (ImplInstanceManager.getAppSearchDir(userHandle).exists()) { // Only clear the package's data if AppSearch exists for this user. PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger(mContext, - userHandle); + userHandle, AppSearchConfig.getInstance(EXECUTOR)); AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext, userHandle, logger); //TODO(b/145759910) clear visibility setting for package. @@ -1147,7 +1147,8 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(callingUser); logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, callingUser); + mContext, callingUser, + AppSearchConfig.getInstance(EXECUTOR)); mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUser, logger); ++operationSuccessCount; invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); @@ -1313,7 +1314,8 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(userHandle); PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, userHandle); + mContext, userHandle, + AppSearchConfig.getInstance(EXECUTOR)); AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl( mContext, userHandle, logger); stats.dataSize += impl.getStorageInfoForPackage(packageName).getSizeBytes(); @@ -1341,7 +1343,8 @@ public class AppSearchManagerService extends SystemService { return; } PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, userHandle); + mContext, userHandle, + AppSearchConfig.getInstance(EXECUTOR)); AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl( mContext, userHandle, logger); for (int i = 0; i < packagesForUid.length; i++) { @@ -1370,7 +1373,8 @@ public class AppSearchManagerService extends SystemService { return; } PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, userHandle); + mContext, userHandle, + AppSearchConfig.getInstance(EXECUTOR)); AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext, userHandle, logger); for (int i = 0; i < packagesForUser.size(); i++) { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java index cb65e42696d1..ea00f506b47f 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java @@ -20,9 +20,9 @@ import android.annotation.NonNull; import android.content.Context; import android.os.UserHandle; import android.util.ArrayMap; -import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; +import com.android.server.appsearch.AppSearchConfig; import com.android.server.appsearch.AppSearchManagerService; import java.util.Map; @@ -34,12 +34,6 @@ import java.util.Objects; * <p>These instances are managed per unique device-user. */ public final class LoggerInstanceManager { - // TODO(b/173532925) flags to control those three - // So probably we can't pass those three in the constructor but need to fetch the latest value - // every time we need them in the logger. - private static final int MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 100; - private static final int DEFAULT_SAMPLING_RATIO = 10; - private static volatile LoggerInstanceManager sLoggerInstanceManager; @GuardedBy("mInstancesLocked") @@ -70,23 +64,19 @@ public final class LoggerInstanceManager { /** * Gets an instance of PlatformLogger for the given user, or creates one if none exists. * - * @param context The context - * @param userHandle The multi-user handle of the device user calling AppSearch + * @param context The context + * @param userHandle The multi-user handle of the device user calling AppSearch * @return An initialized {@link PlatformLogger} for this user */ @NonNull public PlatformLogger getOrCreatePlatformLogger( - @NonNull Context context, @NonNull UserHandle userHandle) { + @NonNull Context context, @NonNull UserHandle userHandle, + @NonNull AppSearchConfig config) { Objects.requireNonNull(userHandle); synchronized (mInstancesLocked) { PlatformLogger instance = mInstancesLocked.get(userHandle); if (instance == null) { - instance = new PlatformLogger(context, userHandle, new PlatformLogger.Config( - MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - DEFAULT_SAMPLING_RATIO, - // TODO(b/173532925) re-enable sampling ratios for different stats types - // once we have P/H flag manager setup in ag/13977824 - /*samplingRatios=*/ new SparseIntArray())); + instance = new PlatformLogger(context, userHandle, config); mInstancesLocked.put(userHandle, instance); } return instance; 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 c857fb602eec..37b67b2fa04b 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 @@ -29,6 +29,7 @@ import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.appsearch.AppSearchConfig; import com.android.server.appsearch.external.localstorage.AppSearchLogger; import com.android.server.appsearch.external.localstorage.stats.CallStats; import com.android.server.appsearch.external.localstorage.stats.InitializeStats; @@ -60,15 +61,15 @@ public final class PlatformLogger implements AppSearchLogger { // User we're logging for. private final UserHandle mUserHandle; - // Configuration for the logger - private final Config mConfig; + // Manager holding the configuration flags + private final AppSearchConfig mConfig; private final Random mRng = new Random(); private final Object mLock = new Object(); /** * SparseArray to track how many stats we skipped due to - * {@link Config#mMinTimeIntervalBetweenSamplesMillis}. + * {@link AppSearchConfig#getCachedMinTimeIntervalBetweenSamplesMillis()}. * * <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: @@ -98,53 +99,6 @@ public final class PlatformLogger implements AppSearchLogger { private long mLastPushTimeMillisLocked = 0; /** - * Class to configure the {@link PlatformLogger} - */ - public static final class Config { - // Minimum time interval (in millis) since last message logged to Westworld before - // logging again. - private final long mMinTimeIntervalBetweenSamplesMillis; - - // Default sampling interval for all types of stats - private final int mDefaultSamplingInterval; - - /** - * 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 interval is missing for certain stats type, - * {@link Config#mDefaultSamplingInterval} will be used. - * - * <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 mSamplingIntervals; - - /** - * Configuration for {@link PlatformLogger} - * - * @param minTimeIntervalBetweenSamplesMillis minimum time interval apart in Milliseconds - * required for two consecutive stats logged - * @param defaultSamplingInterval default sampling interval - * @param samplingIntervals SparseArray to customize sampling interval for - * different stat types - */ - public Config(long minTimeIntervalBetweenSamplesMillis, - 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_INTERVAL_FOR_PUT_DOCUMENTS). - mMinTimeIntervalBetweenSamplesMillis = minTimeIntervalBetweenSamplesMillis; - mDefaultSamplingInterval = defaultSamplingInterval; - mSamplingIntervals = samplingIntervals; - } - } - - /** * Helper class to hold platform specific stats for Westworld. */ static final class ExtraStats { @@ -166,7 +120,8 @@ public final class PlatformLogger implements AppSearchLogger { * Westworld constructor */ public PlatformLogger( - @NonNull Context context, @NonNull UserHandle userHandle, @NonNull Config config) { + @NonNull Context context, @NonNull UserHandle userHandle, + @NonNull AppSearchConfig config) { mContext = Objects.requireNonNull(context); mUserHandle = Objects.requireNonNull(userHandle); mConfig = Objects.requireNonNull(config); @@ -428,9 +383,12 @@ public final class PlatformLogger implements AppSearchLogger { packageUid = getPackageUidAsUserLocked(packageName); } - int samplingInterval = mConfig.mSamplingIntervals.get(callType, - mConfig.mDefaultSamplingInterval); - + // The sampling ratio here might be different from the one used in + // shouldLogForTypeLocked if there is a config change in the middle. + // Since it is only one sample, we can just ignore this difference. + // Or we can retrieve samplingRatio at beginning and pass along + // as function parameter, but it will make code less cleaner with some duplication. + int samplingInterval = getSamplingIntervalFromConfig(callType); int skippedSampleCount = mSkippedSampleCountLocked.get(callType, /*valueOfKeyIfNotFound=*/ 0); mSkippedSampleCountLocked.put(callType, 0); @@ -450,9 +408,7 @@ public final class PlatformLogger implements AppSearchLogger { // rate limiting. @VisibleForTesting boolean shouldLogForTypeLocked(@CallStats.CallType int callType) { - int samplingInterval = mConfig.mSamplingIntervals.get(callType, - mConfig.mDefaultSamplingInterval); - + int samplingInterval = getSamplingIntervalFromConfig(callType); // Sampling if (!shouldSample(samplingInterval)) { return false; @@ -462,7 +418,7 @@ public final class PlatformLogger implements AppSearchLogger { // Check the timestamp to see if it is too close to last logged sample long currentTimeMillis = SystemClock.elapsedRealtime(); if (mLastPushTimeMillisLocked - > currentTimeMillis - mConfig.mMinTimeIntervalBetweenSamplesMillis) { + > currentTimeMillis - mConfig.getCachedMinTimeIntervalBetweenSamplesMillis()) { int count = mSkippedSampleCountLocked.get(callType, /*valueOfKeyIfNotFound=*/ 0); ++count; mSkippedSampleCountLocked.put(callType, count); @@ -502,6 +458,32 @@ public final class PlatformLogger implements AppSearchLogger { return packageUid; } + /** Returns sampling ratio for stats type specified form {@link AppSearchConfig}. */ + private int getSamplingIntervalFromConfig(@CallStats.CallType int statsType) { + switch (statsType) { + case CallStats.CALL_TYPE_PUT_DOCUMENTS: + case CallStats.CALL_TYPE_GET_DOCUMENTS: + case CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_ID: + case CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH: + return mConfig.getCachedSamplingIntervalForBatchCallStats(); + case CallStats.CALL_TYPE_PUT_DOCUMENT: + return mConfig.getCachedSamplingIntervalForPutDocumentStats(); + case CallStats.CALL_TYPE_UNKNOWN: + case CallStats.CALL_TYPE_INITIALIZE: + case CallStats.CALL_TYPE_SET_SCHEMA: + case CallStats.CALL_TYPE_GET_DOCUMENT: + case CallStats.CALL_TYPE_REMOVE_DOCUMENT_BY_ID: + case CallStats.CALL_TYPE_SEARCH: + case CallStats.CALL_TYPE_OPTIMIZE: + case CallStats.CALL_TYPE_FLUSH: + case CallStats.CALL_TYPE_GLOBAL_SEARCH: + case CallStats.CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH: + // TODO(b/173532925) Some of them above will have dedicated sampling ratio config + default: + return mConfig.getCachedSamplingIntervalDefault(); + } + } + // // Functions below are used for tests only // diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 24e11af23798..ff7cd75dd7a1 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -45,6 +45,7 @@ android_test { "service-permission.impl", "service-blobstore", "service-appsearch", + "androidx.test.core", "androidx.test.runner", "androidx.test.ext.truth", "mockito-target-extended-minus-junit4", diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java new file mode 100644 index 000000000000..da0b83ec819c --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java @@ -0,0 +1,235 @@ +/* + * 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.stats; + +import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.DeviceConfig; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.server.appsearch.AppSearchConfig; +import com.android.server.appsearch.external.localstorage.stats.CallStats; +import com.android.server.testables.TestableDeviceConfig; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * Tests covering the functionalities in {@link PlatformLogger} requiring overriding some flags + * in {@link DeviceConfig}. + * + * <p>To add tests NOT rely on overriding the configs, please add them in + * the tests for {@link PlatformLogger} in servicetests. + */ +@RunWith(MockitoJUnitRunner.class) +public class PlatformLoggerTest { + private static final int TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 100; + private static final int TEST_DEFAULT_SAMPLING_INTERVAL = 10; + private static final String TEST_PACKAGE_NAME = "packageName"; + private AppSearchConfig mAppSearchConfig; + + @Rule + public final TestableDeviceConfig.TestableDeviceConfigRule + mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); + + @Before + public void setUp() throws Exception { + mAppSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); + } + + @Test + public void testCreateExtraStatsLocked_samplingIntervalNotSet_returnsDefault() { + PlatformLogger logger = new PlatformLogger( + ApplicationProvider.getApplicationContext(), + UserHandle.of(UserHandle.USER_NULL), + mAppSearchConfig); + + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, + Long.toString(TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Integer.toString(TEST_DEFAULT_SAMPLING_INTERVAL), + false); + + // Make sure default sampling interval is used if there is no config set. + assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, + CallStats.CALL_TYPE_UNKNOWN).mSamplingInterval).isEqualTo( + TEST_DEFAULT_SAMPLING_INTERVAL); + assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, + CallStats.CALL_TYPE_INITIALIZE).mSamplingInterval).isEqualTo( + TEST_DEFAULT_SAMPLING_INTERVAL); + assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, + CallStats.CALL_TYPE_SEARCH).mSamplingInterval).isEqualTo( + TEST_DEFAULT_SAMPLING_INTERVAL); + assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, + CallStats.CALL_TYPE_FLUSH).mSamplingInterval).isEqualTo( + TEST_DEFAULT_SAMPLING_INTERVAL); + } + + + @Test + public void testCreateExtraStatsLocked_samplingIntervalSet_returnsConfigured() { + int putDocumentSamplingInterval = 1; + int batchCallSamplingInterval = 2; + PlatformLogger logger = new PlatformLogger( + ApplicationProvider.getApplicationContext(), + UserHandle.of(UserHandle.USER_NULL), mAppSearchConfig); + + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, + Long.toString(TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Integer.toString(TEST_DEFAULT_SAMPLING_INTERVAL), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, + Integer.toString(putDocumentSamplingInterval), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, + Integer.toString(batchCallSamplingInterval), + false); + + // 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).mSamplingInterval).isEqualTo( + TEST_DEFAULT_SAMPLING_INTERVAL); + assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, + CallStats.CALL_TYPE_FLUSH).mSamplingInterval).isEqualTo( + TEST_DEFAULT_SAMPLING_INTERVAL); + + // 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).mSamplingInterval).isEqualTo( + putDocumentSamplingInterval); + assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, + CallStats.CALL_TYPE_PUT_DOCUMENTS).mSamplingInterval).isEqualTo( + batchCallSamplingInterval); + assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, + CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH).mSamplingInterval).isEqualTo( + batchCallSamplingInterval); + } + + @Test + public void testShouldLogForTypeLocked_trueWhenSampleIntervalIsOne() { + final String testPackageName = "packageName"; + PlatformLogger logger = new PlatformLogger( + ApplicationProvider.getApplicationContext(), + UserHandle.of(UserHandle.USER_NULL), + mAppSearchConfig); + + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Long.toString(1), + false); + + // 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(); + assertThat(logger.createExtraStatsLocked(testPackageName, + CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0); + } + + @Test + public void testShouldLogForTypeLocked_falseWhenSampleIntervalIsNegative() { + final String testPackageName = "packageName"; + PlatformLogger logger = new PlatformLogger( + ApplicationProvider.getApplicationContext(), + UserHandle.of(UserHandle.USER_NULL), + mAppSearchConfig); + + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Long.toString(-1), + false); + + // 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, + CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0); + } + + @Test + public void testShouldLogForTypeLocked_falseWhenWithinCoolOffInterval() { + // Next sample won't be excluded due to sampling. + final int samplingInterval = 1; + // Next sample would guaranteed to be too close. + final int minTimeIntervalBetweenSamplesMillis = Integer.MAX_VALUE; + final String testPackageName = "packageName"; + PlatformLogger logger = new PlatformLogger( + ApplicationProvider.getApplicationContext(), + UserHandle.of(UserHandle.USER_NULL), + mAppSearchConfig); + + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Long.toString(samplingInterval), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, + Long.toString(minTimeIntervalBetweenSamplesMillis), + false); + logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime()); + + // Makes sure sample will be excluded due to rate limiting if samples are too close. + assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse(); + assertThat(logger.createExtraStatsLocked(testPackageName, + CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(1); + } + + @Test + public void testShouldLogForTypeLocked_trueWhenOutsideOfCoolOffInterval() { + // Next sample won't be excluded due to sampling. + final int samplingInterval = 1; + // Next sample would guaranteed to be included. + final int minTimeIntervalBetweenSamplesMillis = 0; + final String testPackageName = "packageName"; + PlatformLogger logger = new PlatformLogger( + ApplicationProvider.getApplicationContext(), + UserHandle.of(UserHandle.USER_NULL), + mAppSearchConfig); + + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, + Long.toString(samplingInterval), + false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, + AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, + Long.toString(minTimeIntervalBetweenSamplesMillis), + false); + logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime()); + + // Makes sure sample will be logged if it is not too close to previous sample. + assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue(); + assertThat(logger.createExtraStatsLocked(testPackageName, + CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0); + } +} 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 7e1639e83ea0..7c275e13e24a 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 @@ -16,6 +16,8 @@ package com.android.server.appsearch.stats; +import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.anyInt; @@ -28,13 +30,12 @@ import android.annotation.NonNull; import android.content.Context; import android.content.ContextWrapper; import android.content.pm.PackageManager; -import android.os.SystemClock; import android.os.UserHandle; import android.util.ArrayMap; -import android.util.SparseIntArray; import androidx.test.core.app.ApplicationProvider; +import com.android.server.appsearch.AppSearchConfig; import com.android.server.appsearch.external.localstorage.stats.CallStats; import org.junit.Before; @@ -48,11 +49,14 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Map; +/** + * Tests covering the functionalities in {@link PlatformLogger} NOT requiring overriding any flags + * in {@link android.provider.DeviceConfig}. + * + * <p>To add tests rely on overriding the flags, please add them in the + * tests for {@link PlatformLogger} in mockingservicestests. + */ public class PlatformLoggerTest { - private static final int TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 100; - private static final int TEST_DEFAULT_SAMPLING_INTERVAL = 10; - private static final String TEST_PACKAGE_NAME = "packageName"; - private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>(); private Context mContext; @@ -73,66 +77,6 @@ public class PlatformLoggerTest { } @Test - 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_INTERVAL, - /*samplingIntervals=*/ new SparseIntArray())); - - // Make sure default sampling interval is used if samplingMap is not provided. - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_UNKNOWN).mSamplingInterval).isEqualTo( - TEST_DEFAULT_SAMPLING_INTERVAL); - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_INITIALIZE).mSamplingInterval).isEqualTo( - TEST_DEFAULT_SAMPLING_INTERVAL); - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_SEARCH).mSamplingInterval).isEqualTo( - TEST_DEFAULT_SAMPLING_INTERVAL); - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_FLUSH).mSamplingInterval).isEqualTo( - TEST_DEFAULT_SAMPLING_INTERVAL); - } - - - @Test - 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_INTERVAL, - samplingIntervals)); - - // 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).mSamplingInterval).isEqualTo( - TEST_DEFAULT_SAMPLING_INTERVAL); - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_FLUSH).mSamplingInterval).isEqualTo( - TEST_DEFAULT_SAMPLING_INTERVAL); - - // 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).mSamplingInterval).isEqualTo( - putDocumentSamplingInterval); - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_SEARCH).mSamplingInterval).isEqualTo( - querySamplingInterval); - } - - @Test public void testCalculateHashCode_MD5_int32_shortString() throws NoSuchAlgorithmException, UnsupportedEncodingException { final String str1 = "d1"; @@ -202,87 +146,6 @@ public class PlatformLoggerTest { assertThat(PlatformLogger.calculateHashCodeMd5(/*str=*/ null)).isEqualTo(-1); } - @Test - 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, - 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(); - assertThat(logger.createExtraStatsLocked(testPackageName, - CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0); - } - - @Test - 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, - samplingInterval, - /*samplingIntervals=*/ new SparseIntArray())); - - // 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, - CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0); - } - - @Test - public void testShouldLogForTypeLocked_falseWhenWithinCoolOffInterval() { - // Next sample won't be excluded due to sampling. - final int samplingInterval = 1; - // Next sample would guaranteed to be too close. - final int minTimeIntervalBetweenSamplesMillis = Integer.MAX_VALUE; - final String testPackageName = "packageName"; - PlatformLogger logger = new PlatformLogger( - ApplicationProvider.getApplicationContext(), - UserHandle.of(UserHandle.USER_NULL), - new PlatformLogger.Config( - minTimeIntervalBetweenSamplesMillis, - samplingInterval, - /*samplingIntervals=*/ new SparseIntArray())); - logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime()); - - // Makes sure sample will be excluded due to rate limiting if samples are too close. - assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse(); - assertThat(logger.createExtraStatsLocked(testPackageName, - CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(1); - } - - @Test - public void testShouldLogForTypeLocked_trueWhenOutsideOfCoolOffInterval() { - // Next sample won't be excluded due to sampling. - final int samplingInterval = 1; - // Next sample would guaranteed to be included. - final int minTimeIntervalBetweenSamplesMillis = 0; - final String testPackageName = "packageName"; - PlatformLogger logger = new PlatformLogger( - ApplicationProvider.getApplicationContext(), - UserHandle.of(UserHandle.USER_NULL), - new PlatformLogger.Config( - minTimeIntervalBetweenSamplesMillis, - samplingInterval, - /*samplingIntervals=*/ new SparseIntArray())); - logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime()); - - // Makes sure sample will be logged if it is not too close to previous sample. - assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue(); - assertThat(logger.createExtraStatsLocked(testPackageName, - CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0); - } - /** Makes sure the caching works while getting the UID for calling package. */ @Test public void testGetPackageUidAsUser() throws Exception { @@ -291,10 +154,7 @@ public class PlatformLoggerTest { PlatformLogger logger = new PlatformLogger( mContext, mContext.getUser(), - new PlatformLogger.Config( - TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - TEST_DEFAULT_SAMPLING_INTERVAL, - /*samplingIntervals=*/ new SparseIntArray())); + AppSearchConfig.create(DIRECT_EXECUTOR)); PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); when(mockPackageManager.getPackageUid(testPackageName, /*flags=*/0)).thenReturn(testUid); |