Use AppSearchConfig in PlatformLogger

bug: 173532925
Test: atest CtsAppSearchTestCases FrameworksCoreTests:android.app.appsearch
FrameworksServicesTests:AppSearchImplTest
FrameworksServicesTests:com.android.server.appsearch.stats.PlatformLoggerTest
FrameworksMockingServicesTests:com.android.server.appsearch.AppSearchConfigTest

Change-Id: I3589f0071d456e2167cd207e83acaf1d884f9992
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 6e81afc..d5271a6 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 @@
  * @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 @@
                 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 @@
         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 0709ff5..a3b89b0 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 @@
             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 @@
                 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 @@
             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 @@
                     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 @@
                     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 cb65e42..ea00f50 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.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 @@
  * <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 @@
     /**
      * 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 c857fb6..37b67b2 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 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 @@
     // 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 @@
     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 @@
      * 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 @@
             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 @@
     // 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 @@
         // 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 @@
         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 24e11af..ff7cd75 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -45,6 +45,7 @@
         "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 0000000..da0b83e
--- /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 7e1639e..7c275e1 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.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.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 @@
     }
 
     @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 @@
         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 @@
         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);