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

Commit 6d44d960 authored by Xiaoyu Jin's avatar Xiaoyu Jin Committed by Android (Google) Code Review
Browse files

Merge "Use the PlatformLogger in the platform" into sc-dev

parents 9e2adae4 79119ac9
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.app.appsearch.util.SchemaMigrationUtil;
import android.os.Bundle;
import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -270,8 +271,8 @@ public final class AppSearchSession implements Closeable {
            documentBundles.add(documents.get(i).getBundle());
        }
        try {
            // TODO(b/173532925) a timestamp needs to be sent here to calculate binder latency
            mService.putDocuments(mPackageName, mDatabaseName, documentBundles, mUserId,
                    /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
                    new IAppSearchBatchResultCallback.Stub() {
                        public void onResult(AppSearchBatchResult result) {
                            executor.execute(() -> callback.onResult(result));
+2 −0
Original line number Diff line number Diff line
@@ -94,6 +94,7 @@ interface IAppSearchManager {
     * @param databaseName  The name of the database where this document lives.
     * @param documentBundes List of GenericDocument bundles.
     * @param userId Id of the calling user
     * @param binderCallStartTimeMillis start timestamp of binder call in Millis
     * @param callback
     *     If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError}
     *     will be called with the cause throwable. Otherwise,
@@ -106,6 +107,7 @@ interface IAppSearchManager {
        in String databaseName,
        in List<Bundle> documentBundles,
        in int userId,
        in long binderCallStartTimeMillis,
        in IAppSearchBatchResultCallback callback);

    /**
+43 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.appsearch;
import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
import static android.os.UserHandle.USER_NULL;

import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -45,6 +46,7 @@ import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
@@ -57,6 +59,9 @@ import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.stats.LoggerInstanceManager;
import com.android.server.appsearch.stats.PlatformLogger;

import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -80,6 +85,7 @@ public class AppSearchManagerService extends SystemService {
    private PackageManagerInternal mPackageManagerInternal;
    private ImplInstanceManager mImplInstanceManager;
    private UserManager mUserManager;
    private LoggerInstanceManager mLoggerInstanceManager;

    // Never call shutdownNow(). It will cancel the futures it's returned. And since
    // Executor#execute won't return anything, we will hang forever waiting for the execution.
@@ -106,6 +112,7 @@ public class AppSearchManagerService extends SystemService {
        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
        mImplInstanceManager = ImplInstanceManager.getInstance(mContext);
        mUserManager = mContext.getSystemService(UserManager.class);
        mLoggerInstanceManager = LoggerInstanceManager.getInstance();
        registerReceivers();
    }

@@ -147,6 +154,7 @@ public class AppSearchManagerService extends SystemService {
    private void handleUserRemoved(@UserIdInt int userId) {
        try {
            mImplInstanceManager.removeAppSearchImplForUser(userId);
            mLoggerInstanceManager.removePlatformLoggerForUser(userId);
            Slog.i(TAG, "Removed AppSearchImpl instance for user: " + userId);
        } catch (Throwable t) {
            Slog.e(TAG, "Unable to remove data for user: " + userId, t);
@@ -274,6 +282,7 @@ public class AppSearchManagerService extends SystemService {
                @NonNull String databaseName,
                @NonNull List<Bundle> documentBundles,
                @UserIdInt int userId,
                @ElapsedRealtimeLong long binderCallStartTimeMillis,
                @NonNull IAppSearchBatchResultCallback callback) {
            Preconditions.checkNotNull(packageName);
            Preconditions.checkNotNull(databaseName);
@@ -282,6 +291,11 @@ public class AppSearchManagerService extends SystemService {
            int callingUid = Binder.getCallingUid();
            int callingUserId = handleIncomingUser(userId, callingUid);
            EXECUTOR.execute(() -> {
                long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
                @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
                PlatformLogger logger = null;
                int operationSuccessCount = 0;
                int operationFailureCount = 0;
                try {
                    verifyUserUnlocked(callingUserId);
                    verifyCallingPackage(callingUid, packageName);
@@ -289,20 +303,46 @@ public class AppSearchManagerService extends SystemService {
                            new AppSearchBatchResult.Builder<>();
                    AppSearchImpl impl =
                            mImplInstanceManager.getAppSearchImpl(callingUserId);
                    logger = mLoggerInstanceManager.getPlatformLogger(callingUserId);
                    for (int i = 0; i < documentBundles.size(); i++) {
                        GenericDocument document = new GenericDocument(documentBundles.get(i));
                        try {
                            impl.putDocument(packageName, databaseName, document,
                                    /*logger=*/ null);
                            impl.putDocument(packageName, databaseName, document, logger);
                            resultBuilder.setSuccess(document.getUri(), /*result=*/ null);
                            ++operationSuccessCount;
                        } catch (Throwable t) {
                            resultBuilder.setResult(document.getUri(),
                                    throwableToFailedResult(t));
                            AppSearchResult<Void> result = throwableToFailedResult(t);
                            resultBuilder.setResult(document.getUri(), result);
                            // for failures, we would just log the one for last failure
                            statusCode = result.getResultCode();
                            ++operationFailureCount;
                        }
                    }
                    invokeCallbackOnResult(callback, resultBuilder.build());
                } catch (Throwable t) {
                    invokeCallbackOnError(callback, t);
                } finally {
                    if (logger != null) {
                        CallStats.Builder cBuilder = new CallStats.Builder(packageName,
                                databaseName)
                                .setCallType(CallStats.CALL_TYPE_PUT_DOCUMENTS)
                                // TODO(b/173532925) check the existing binder call latency chart
                                // is good enough for us:
                                // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
                                .setEstimatedBinderLatencyMillis(
                                        2 * (int) (totalLatencyStartTimeMillis
                                                - binderCallStartTimeMillis))
                                .setNumOperationsSucceeded(operationSuccessCount)
                                .setNumOperationsFailed(operationFailureCount);
                        cBuilder.getGeneralStatsBuilder()
                                .setStatusCode(statusCode)
                                .setTotalLatencyMillis(
                                        (int) (SystemClock.elapsedRealtime()
                                                - totalLatencyStartTimeMillis));
                        logger.logStats(cBuilder.build());
                    }
                }
            });
        }
@@ -717,6 +757,7 @@ public class AppSearchManagerService extends SystemService {
                try {
                    verifyUserUnlocked(callingUserId);
                    mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUserId);
                    mLoggerInstanceManager.getOrCreatePlatformLogger(getContext(), callingUserId);
                    invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
                } catch (Throwable t) {
                    invokeCallbackOnError(callback, t);
+0 −1
Original line number Diff line number Diff line
@@ -106,7 +106,6 @@ public final class ImplInstanceManager {
     *
     * @param userId The multi-user userId of the user that need to be removed.
     */
    @NonNull
    public void removeAppSearchImplForUser(@UserIdInt int userId) {
        synchronized (mInstancesLocked) {
            mInstancesLocked.remove(userId);
+131 −0
Original line number Diff line number Diff line
/*
 * 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 android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
import android.util.SparseArray;
import android.util.SparseIntArray;

import com.android.internal.annotations.GuardedBy;
import com.android.server.appsearch.AppSearchManagerService;

/**
 * Manages the lifecycle of instances of {@link PlatformLogger}.
 *
 * <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")
    private final SparseArray<PlatformLogger> mInstancesLocked = new SparseArray<>();

    private LoggerInstanceManager() {
    }

    /**
     * Gets an instance of {@link LoggerInstanceManager} 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 LoggerInstanceManager getInstance() {
        if (sLoggerInstanceManager == null) {
            synchronized (LoggerInstanceManager.class) {
                if (sLoggerInstanceManager == null) {
                    sLoggerInstanceManager =
                            new LoggerInstanceManager();
                }
            }
        }
        return sLoggerInstanceManager;
    }

    /**
     * Gets an instance of PlatformLogger for the given user, or creates one if none exists.
     *
     * @param context The context
     * @param userId  The multi-user userId of the device user calling AppSearch
     * @return An initialized {@link PlatformLogger} for this user
     */
    @NonNull
    public PlatformLogger getOrCreatePlatformLogger(
            @NonNull Context context, @UserIdInt int userId) {
        synchronized (mInstancesLocked) {
            PlatformLogger instance = mInstancesLocked.get(userId);
            if (instance == null) {
                instance = new PlatformLogger(context, userId, 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()));
                mInstancesLocked.put(userId, instance);
            }
            return instance;
        }
    }


    /**
     * Gets an instance of PlatformLogger for the given user.
     *
     * <p>This method should only be called by an initialized SearchSession, which has been already
     * created the PlatformLogger instance for the given user.
     *
     * @param userId The multi-user userId of the device user calling AppSearch
     * @return An initialized {@link PlatformLogger} for this user
     * @throws IllegalStateException if {@link PlatformLogger} haven't created for the given user.
     */
    @NonNull
    public PlatformLogger getPlatformLogger(@UserIdInt int userId) {
        synchronized (mInstancesLocked) {
            PlatformLogger instance = mInstancesLocked.get(userId);
            if (instance == null) {
                // Impossible scenario, user cannot call an uninitialized SearchSession,
                // getInstance should always find the instance for the given user and never try to
                // create an instance for this user again.
                throw new IllegalStateException(
                        "PlatformLogger has never been created for this user: " + userId);
            }
            return instance;
        }
    }

    /**
     * Remove an instance of {@link PlatformLogger} for the given user.
     *
     * <p>This method should only be called if {@link AppSearchManagerService} receives an
     * ACTION_USER_REMOVED, which the logger instance of given user should be removed.
     *
     * @param userId The multi-user userId of the user that need to be removed.
     */
    public void removePlatformLoggerForUser(@UserIdInt int userId) {
        synchronized (mInstancesLocked) {
            mInstancesLocked.remove(userId);
        }
    }
}
Loading