Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c5d9874 authored by Xiaoyu Jin's avatar Xiaoyu Jin
Browse files

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
parent 5ba9720d
Loading
Loading
Loading
Loading
+23 −1
Original line number Diff line number Diff line
@@ -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;
    }

    /**
+9 −5
Original line number Diff line number Diff line
@@ -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++) {
+6 −16
Original line number Diff line number Diff line
@@ -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")
@@ -76,17 +70,13 @@ public final class LoggerInstanceManager {
     */
    @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;
+40 −58
Original line number Diff line number Diff line
@@ -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);
@@ -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
    //
+1 −0
Original line number Diff line number Diff line
@@ -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",
Loading