Merge "Revert "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 c3b7087..1f98334 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
@@ -16,7 +16,15 @@
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;
@@ -33,5 +41,50 @@
/* 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 1e723b5..b4713d9 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -95,7 +95,12 @@
public void onUserStopping(@NonNull TargetUser user) {
Objects.requireNonNull(user);
- MetadataSyncPerUser.removeUserSyncAdapter(user.getUserHandle());
+ try {
+ AppFunctionExecutors.shutDownAndRemoveUserExecutor(user.getUserHandle());
+ MetadataSyncPerUser.removeUserSyncAdapter(user.getUserHandle());
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Unable to remove data for: " + user.getUserHandle(), e);
+ }
}
@Override
diff --git a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
index 759f02e..e29b6e4 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
@@ -42,7 +42,6 @@
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;
@@ -53,12 +52,8 @@
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.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.Executor;
/**
* This class implements helper methods for synchronously interacting with AppSearch while
@@ -68,14 +63,9 @@
*/
public class MetadataSyncAdapter {
private static final String TAG = MetadataSyncAdapter.class.getSimpleName();
-
- private final ExecutorService mExecutor;
-
+ private final Executor mSyncExecutor;
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.
@@ -83,10 +73,12 @@
public static final int EXECUTE_APP_FUNCTIONS_TRUSTED = 10;
public MetadataSyncAdapter(
- @NonNull PackageManager packageManager, @NonNull AppSearchManager appSearchManager) {
+ @NonNull Executor syncExecutor,
+ @NonNull PackageManager packageManager,
+ @NonNull AppSearchManager appSearchManager) {
+ mSyncExecutor = Objects.requireNonNull(syncExecutor);
mPackageManager = Objects.requireNonNull(packageManager);
mAppSearchManager = Objects.requireNonNull(appSearchManager);
- mExecutor = Executors.newSingleThreadExecutor();
}
/**
@@ -105,7 +97,7 @@
AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_METADATA_DB)
.build();
AndroidFuture<Boolean> settableSyncStatus = new AndroidFuture<>();
- Callable<AndroidFuture<Boolean>> callableTask =
+ mSyncExecutor.execute(
() -> {
try (FutureAppSearchSession staticMetadataSearchSession =
new FutureAppSearchSessionImpl(
@@ -125,28 +117,10 @@
} 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 e933ec1..f421527 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncPerUser.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncPerUser.java
@@ -55,7 +55,10 @@
PackageManager perUserPackageManager = userContext.getPackageManager();
if (perUserAppSearchManager != null) {
metadataSyncAdapter =
- new MetadataSyncAdapter(perUserPackageManager, perUserAppSearchManager);
+ new MetadataSyncAdapter(
+ AppFunctionExecutors.getPerUserSyncExecutor(user),
+ perUserPackageManager,
+ perUserAppSearchManager);
sPerUserMetadataSyncAdapter.put(user.getIdentifier(), metadataSyncAdapter);
return metadataSyncAdapter;
}
@@ -71,12 +74,7 @@
*/
public static void removeUserSyncAdapter(UserHandle user) {
synchronized (sLock) {
- MetadataSyncAdapter metadataSyncAdapter =
- sPerUserMetadataSyncAdapter.get(user.getIdentifier(), null);
- if (metadataSyncAdapter != null) {
- metadataSyncAdapter.shutDown();
- sPerUserMetadataSyncAdapter.remove(user.getIdentifier());
- }
+ 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 bc64e15..c05c381 100644
--- a/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
@@ -36,6 +36,7 @@
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
@@ -45,6 +46,7 @@
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
@@ -136,7 +138,8 @@
PutDocumentsRequest.Builder().addGenericDocuments(functionRuntimeMetadata).build()
runtimeSearchSession.put(putDocumentsRequest).get()
staticSearchSession.put(putDocumentsRequest).get()
- val metadataSyncAdapter = MetadataSyncAdapter(packageManager, appSearchManager)
+ val metadataSyncAdapter =
+ MetadataSyncAdapter(testExecutor, packageManager, appSearchManager)
val submitSyncRequest =
metadataSyncAdapter.trySyncAppFunctionMetadataBlocking(
@@ -177,7 +180,8 @@
val putDocumentsRequest: PutDocumentsRequest =
PutDocumentsRequest.Builder().addGenericDocuments(functionRuntimeMetadata).build()
staticSearchSession.put(putDocumentsRequest).get()
- val metadataSyncAdapter = MetadataSyncAdapter(packageManager, appSearchManager)
+ val metadataSyncAdapter =
+ MetadataSyncAdapter(testExecutor, packageManager, appSearchManager)
val submitSyncRequest =
metadataSyncAdapter.trySyncAppFunctionMetadataBlocking(
@@ -232,7 +236,8 @@
val putDocumentsRequest: PutDocumentsRequest =
PutDocumentsRequest.Builder().addGenericDocuments(functionRuntimeMetadata).build()
runtimeSearchSession.put(putDocumentsRequest).get()
- val metadataSyncAdapter = MetadataSyncAdapter(packageManager, appSearchManager)
+ val metadataSyncAdapter =
+ MetadataSyncAdapter(testExecutor, packageManager, appSearchManager)
val submitSyncRequest =
metadataSyncAdapter.trySyncAppFunctionMetadataBlocking(