Merge "Use AppSearchConfig in PlatformLogger" into sc-dev

This commit is contained in:
Xiaoyu Jin
2021-06-09 20:26:47 +00:00
committed by Android (Google) Code Review
7 changed files with 325 additions and 231 deletions

View File

@@ -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;
}
/**

View File

@@ -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++) {

View File

@@ -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;

View File

@@ -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:
@@ -97,53 +98,6 @@ public final class PlatformLogger implements AppSearchLogger {
@GuardedBy("mLock")
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.
*/
@@ -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);
@@ -430,9 +385,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);
@@ -452,9 +410,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;
@@ -464,7 +420,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);
@@ -504,6 +460,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
//

View File

@@ -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",

View File

@@ -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);
}
}

View File

@@ -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;
@@ -72,66 +76,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 {
@@ -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);