Merge "Batch calls to the Metadata Syncer" into main
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
index 1f98334..c3b7087 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
@@ -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 @@
/* 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() {}
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 469a1a2..165a945 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -95,12 +95,7 @@
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);
- }
+ MetadataSyncPerUser.removeUserSyncAdapter(user.getUserHandle());
}
@Override
diff --git a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
index e29b6e4..759f02e 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
@@ -42,6 +42,7 @@
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.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 @@
*/
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 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 @@
AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_METADATA_DB)
.build();
AndroidFuture<Boolean> settableSyncStatus = new AndroidFuture<>();
- mSyncExecutor.execute(
+ Callable<AndroidFuture<Boolean>> callableTask =
() -> {
try (FutureAppSearchSession staticMetadataSearchSession =
new FutureAppSearchSessionImpl(
@@ -117,10 +125,28 @@
} 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
@VisibleForTesting
void trySyncAppFunctionMetadataBlocking(
diff --git a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncPerUser.java b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncPerUser.java
index f421527..e933ec1 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncPerUser.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncPerUser.java
@@ -55,10 +55,7 @@
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 static void removeUserSyncAdapter(UserHandle user) {
synchronized (sLock) {
- sPerUserMetadataSyncAdapter.remove(user.getIdentifier());
+ MetadataSyncAdapter metadataSyncAdapter =
+ sPerUserMetadataSyncAdapter.get(user.getIdentifier(), null);
+ if (metadataSyncAdapter != null) {
+ metadataSyncAdapter.shutDown();
+ sPerUserMetadataSyncAdapter.remove(user.getIdentifier());
+ }
}
}
}
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
index c05c381..bc64e15 100644
--- a/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
@@ -36,7 +36,6 @@
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 @@
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 @@
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 @@
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 @@
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(