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/service/java/com/android/server/appsearch/AppSearchConfig.java b/service/java/com/android/server/appsearch/AppSearchConfig.java
index 6e81afc..d5271a6 100644
--- a/service/java/com/android/server/appsearch/AppSearchConfig.java
+++ b/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/service/java/com/android/server/appsearch/AppSearchManagerService.java b/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 0709ff5..a3b89b0 100644
--- a/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/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/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java b/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java
index cb65e42..ea00f50 100644
--- a/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java
+++ b/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/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/service/java/com/android/server/appsearch/stats/PlatformLogger.java
index c857fb6..37b67b2 100644
--- a/service/java/com/android/server/appsearch/stats/PlatformLogger.java
+++ b/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
//