Loading services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java +53 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -33,5 +41,50 @@ 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() {} } services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java +6 −1 Original line number Diff line number Diff line Loading @@ -95,7 +95,12 @@ 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 Loading services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java +8 −34 Original line number Diff line number Diff line Loading @@ -42,7 +42,6 @@ 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; Loading @@ -53,12 +52,8 @@ 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.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 Loading @@ -68,14 +63,9 @@ import java.util.concurrent.TimeUnit; */ 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. Loading @@ -83,10 +73,12 @@ public class MetadataSyncAdapter { 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(); } /** Loading @@ -105,7 +97,7 @@ public class MetadataSyncAdapter { AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_METADATA_DB) .build(); AndroidFuture<Boolean> settableSyncStatus = new AndroidFuture<>(); Callable<AndroidFuture<Boolean>> callableTask = mSyncExecutor.execute( () -> { try (FutureAppSearchSession staticMetadataSearchSession = new FutureAppSearchSessionImpl( Loading @@ -125,26 +117,8 @@ 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 Loading services/appfunctions/java/com/android/server/appfunctions/MetadataSyncPerUser.java +5 −7 Original line number Diff line number Diff line Loading @@ -55,7 +55,10 @@ public final class MetadataSyncPerUser { 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; } Loading @@ -71,12 +74,7 @@ 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()); } } } } services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt +8 −3 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ 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 Loading @@ -45,6 +46,7 @@ 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 Loading Loading @@ -136,7 +138,8 @@ class MetadataSyncAdapterTest { 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( Loading Loading @@ -177,7 +180,8 @@ class MetadataSyncAdapterTest { 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( Loading Loading @@ -232,7 +236,8 @@ class MetadataSyncAdapterTest { 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( Loading Loading
services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java +53 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -33,5 +41,50 @@ 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() {} }
services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java +6 −1 Original line number Diff line number Diff line Loading @@ -95,7 +95,12 @@ 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 Loading
services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java +8 −34 Original line number Diff line number Diff line Loading @@ -42,7 +42,6 @@ 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; Loading @@ -53,12 +52,8 @@ 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.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 Loading @@ -68,14 +63,9 @@ import java.util.concurrent.TimeUnit; */ 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. Loading @@ -83,10 +73,12 @@ public class MetadataSyncAdapter { 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(); } /** Loading @@ -105,7 +97,7 @@ public class MetadataSyncAdapter { AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_METADATA_DB) .build(); AndroidFuture<Boolean> settableSyncStatus = new AndroidFuture<>(); Callable<AndroidFuture<Boolean>> callableTask = mSyncExecutor.execute( () -> { try (FutureAppSearchSession staticMetadataSearchSession = new FutureAppSearchSessionImpl( Loading @@ -125,26 +117,8 @@ 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 Loading
services/appfunctions/java/com/android/server/appfunctions/MetadataSyncPerUser.java +5 −7 Original line number Diff line number Diff line Loading @@ -55,7 +55,10 @@ public final class MetadataSyncPerUser { 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; } Loading @@ -71,12 +74,7 @@ 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()); } } } }
services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt +8 −3 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ 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 Loading @@ -45,6 +46,7 @@ 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 Loading Loading @@ -136,7 +138,8 @@ class MetadataSyncAdapterTest { 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( Loading Loading @@ -177,7 +180,8 @@ class MetadataSyncAdapterTest { 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( Loading Loading @@ -232,7 +236,8 @@ class MetadataSyncAdapterTest { 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( Loading