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

Commit 3c39767a authored by Desh's avatar Desh Committed by Tony Mak
Browse files

Batch calls to the Metadata Syncer

Context: Sometimes we can have consecutive calls requesting a sync, originating from a single batch of updates.
Flag: android.app.appfunctions.flags.enable_app_function_manager
Test: atest FrameworksAppFunctionsTests -c + cts
Bug: 357551503
Change-Id: I2b2ef8902aa50e25aaf2ec9b26045d75725a728c
parent b2af38ed
Loading
Loading
Loading
Loading
+0 −53
Original line number Diff line number Diff line
@@ -16,15 +16,7 @@

package com.android.server.appfunctions;

import android.annotation.NonNull;
import android.os.UserHandle;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -41,50 +33,5 @@ public final class AppFunctionExecutors {
                    /* unit= */ TimeUnit.SECONDS,
                    /* workQueue= */ new LinkedBlockingQueue<>());

    /** A map of per-user executors for queued work. */
    @GuardedBy("sLock")
    private static final SparseArray<ExecutorService> mPerUserExecutorsLocked = new SparseArray<>();

    private static final Object sLock = new Object();

    /**
     * Returns a per-user executor for queued metadata sync request.
     *
     * <p>The work submitted to these executor (Sync request) needs to be synchronous per user hence
     * the use of a single thread.
     *
     * <p>Note: Use a different executor if not calling {@code submitSyncRequest} on a {@code
     * MetadataSyncAdapter}.
     */
    // TODO(b/357551503): Restrict the scope of this executor to the MetadataSyncAdapter itself.
    public static ExecutorService getPerUserSyncExecutor(@NonNull UserHandle user) {
        synchronized (sLock) {
            ExecutorService executor = mPerUserExecutorsLocked.get(user.getIdentifier(), null);
            if (executor == null) {
                executor = Executors.newSingleThreadExecutor();
                mPerUserExecutorsLocked.put(user.getIdentifier(), executor);
            }
            return executor;
        }
    }

    /**
     * Shuts down and removes the per-user executor for queued work.
     *
     * <p>This should be called when the user is removed.
     */
    public static void shutDownAndRemoveUserExecutor(@NonNull UserHandle user)
            throws InterruptedException {
        ExecutorService executor;
        synchronized (sLock) {
            executor = mPerUserExecutorsLocked.get(user.getIdentifier());
            mPerUserExecutorsLocked.remove(user.getIdentifier());
        }
        if (executor != null) {
            executor.shutdown();
            var unused = executor.awaitTermination(30, TimeUnit.SECONDS);
        }
    }

    private AppFunctionExecutors() {}
}
+1 −6
Original line number Diff line number Diff line
@@ -95,12 +95,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
    public void onUserStopping(@NonNull TargetUser user) {
        Objects.requireNonNull(user);

        try {
            AppFunctionExecutors.shutDownAndRemoveUserExecutor(user.getUserHandle());
        MetadataSyncPerUser.removeUserSyncAdapter(user.getUserHandle());
        } catch (InterruptedException e) {
            Slog.e(TAG, "Unable to remove data for: " + user.getUserHandle(), e);
        }
    }

    @Override
+34 −8
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
import com.android.server.appfunctions.FutureAppSearchSession.FutureSearchResults;
@@ -52,8 +53,12 @@ import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * This class implements helper methods for synchronously interacting with AppSearch while
@@ -63,9 +68,14 @@ import java.util.concurrent.Executor;
 */
public class MetadataSyncAdapter {
    private static final String TAG = MetadataSyncAdapter.class.getSimpleName();
    private final Executor mSyncExecutor;

    private final ExecutorService mExecutor;

    private final AppSearchManager mAppSearchManager;
    private final PackageManager mPackageManager;
    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private Future<AndroidFuture<Boolean>> mCurrentSyncTask;

    // Hidden constants in {@link SetSchemaRequest} that restricts runtime metadata visibility
    // by permissions.
@@ -73,12 +83,10 @@ public class MetadataSyncAdapter {
    public static final int EXECUTE_APP_FUNCTIONS_TRUSTED = 10;

    public MetadataSyncAdapter(
            @NonNull Executor syncExecutor,
            @NonNull PackageManager packageManager,
            @NonNull AppSearchManager appSearchManager) {
        mSyncExecutor = Objects.requireNonNull(syncExecutor);
            @NonNull PackageManager packageManager, @NonNull AppSearchManager appSearchManager) {
        mPackageManager = Objects.requireNonNull(packageManager);
        mAppSearchManager = Objects.requireNonNull(appSearchManager);
        mExecutor = Executors.newSingleThreadExecutor();
    }

    /**
@@ -97,7 +105,7 @@ public class MetadataSyncAdapter {
                                AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_METADATA_DB)
                        .build();
        AndroidFuture<Boolean> settableSyncStatus = new AndroidFuture<>();
        mSyncExecutor.execute(
        Callable<AndroidFuture<Boolean>> callableTask =
                () -> {
                    try (FutureAppSearchSession staticMetadataSearchSession =
                                    new FutureAppSearchSessionImpl(
@@ -117,8 +125,26 @@ public class MetadataSyncAdapter {
                    } catch (Exception ex) {
                        settableSyncStatus.completeExceptionally(ex);
                    }
                });
                    return settableSyncStatus;
                };

        synchronized (mLock) {
            if (mCurrentSyncTask != null && !mCurrentSyncTask.isDone()) {
                boolean cancel = mCurrentSyncTask.cancel(false);
            }
            mCurrentSyncTask = mExecutor.submit(callableTask);
        }

        return settableSyncStatus;
    }

    /** This method shuts down the {@link MetadataSyncAdapter} scheduler. */
    public void shutDown() {
        try {
            var unused = mExecutor.awaitTermination(30, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Slog.e(TAG, "Error shutting down MetadataSyncAdapter scheduler", e);
        }
    }

    @WorkerThread
+7 −5
Original line number Diff line number Diff line
@@ -55,10 +55,7 @@ public final class MetadataSyncPerUser {
                PackageManager perUserPackageManager = userContext.getPackageManager();
                if (perUserAppSearchManager != null) {
                    metadataSyncAdapter =
                            new MetadataSyncAdapter(
                                    AppFunctionExecutors.getPerUserSyncExecutor(user),
                                    perUserPackageManager,
                                    perUserAppSearchManager);
                            new MetadataSyncAdapter(perUserPackageManager, perUserAppSearchManager);
                    sPerUserMetadataSyncAdapter.put(user.getIdentifier(), metadataSyncAdapter);
                    return metadataSyncAdapter;
                }
@@ -74,7 +71,12 @@ public final class MetadataSyncPerUser {
     */
    public static void removeUserSyncAdapter(UserHandle user) {
        synchronized (sLock) {
            MetadataSyncAdapter metadataSyncAdapter =
                    sPerUserMetadataSyncAdapter.get(user.getIdentifier(), null);
            if (metadataSyncAdapter != null) {
                metadataSyncAdapter.shutDown();
                sPerUserMetadataSyncAdapter.remove(user.getIdentifier());
            }
        }
    }
}
+3 −8
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import androidx.test.platform.app.InstrumentationRegistry
import com.android.internal.infra.AndroidFuture
import com.android.server.appfunctions.FutureAppSearchSession.FutureSearchResults
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors
import java.util.concurrent.atomic.AtomicBoolean
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,7 +45,6 @@ import org.junit.runners.JUnit4
class MetadataSyncAdapterTest {
    private val context = InstrumentationRegistry.getInstrumentation().targetContext
    private val appSearchManager = context.getSystemService(AppSearchManager::class.java)
    private val testExecutor = MoreExecutors.directExecutor()
    private val packageManager = context.packageManager

    @Test
@@ -138,8 +136,7 @@ class MetadataSyncAdapterTest {
            PutDocumentsRequest.Builder().addGenericDocuments(functionRuntimeMetadata).build()
        runtimeSearchSession.put(putDocumentsRequest).get()
        staticSearchSession.put(putDocumentsRequest).get()
        val metadataSyncAdapter =
            MetadataSyncAdapter(testExecutor, packageManager, appSearchManager)
        val metadataSyncAdapter = MetadataSyncAdapter(packageManager, appSearchManager)

        val submitSyncRequest =
            metadataSyncAdapter.trySyncAppFunctionMetadataBlocking(
@@ -180,8 +177,7 @@ class MetadataSyncAdapterTest {
        val putDocumentsRequest: PutDocumentsRequest =
            PutDocumentsRequest.Builder().addGenericDocuments(functionRuntimeMetadata).build()
        staticSearchSession.put(putDocumentsRequest).get()
        val metadataSyncAdapter =
            MetadataSyncAdapter(testExecutor, packageManager, appSearchManager)
        val metadataSyncAdapter = MetadataSyncAdapter(packageManager, appSearchManager)

        val submitSyncRequest =
            metadataSyncAdapter.trySyncAppFunctionMetadataBlocking(
@@ -236,8 +232,7 @@ class MetadataSyncAdapterTest {
        val putDocumentsRequest: PutDocumentsRequest =
            PutDocumentsRequest.Builder().addGenericDocuments(functionRuntimeMetadata).build()
        runtimeSearchSession.put(putDocumentsRequest).get()
        val metadataSyncAdapter =
            MetadataSyncAdapter(testExecutor, packageManager, appSearchManager)
        val metadataSyncAdapter = MetadataSyncAdapter(packageManager, appSearchManager)

        val submitSyncRequest =
            metadataSyncAdapter.trySyncAppFunctionMetadataBlocking(