Loading services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java +42 −7 Original line number Diff line number Diff line Loading @@ -414,6 +414,16 @@ public class MetadataSyncAdapter { * This method returns a map of package names to a set of function ids from the AppFunction * metadata. * * <p>Retry Conditions: * * <ul> * <li>If an {@link AppSearchException} with {@code RESULT_ABORTED} (code 13) is thrown during * the first attempt, the query will be retried once. * <li>If the number of function ids returned equals {@code DEFAULT_RESULT_COUNT_PER_PAGE}, * which may indicate an incomplete result due to known issue b/400670498, the query will * be retried with an increased page size ({@code RETRY_RESULT_COUNT_PER_PAGE}). * </ul> * * @param searchSession The {@link FutureAppSearchSession} to search the AppFunction metadata. * @param schemaType The schema type of the AppFunction metadata. * @param propertyFunctionId The property name of the function id in the AppFunction metadata. Loading @@ -429,13 +439,38 @@ public class MetadataSyncAdapter { @NonNull String propertyFunctionId, @NonNull String propertyPackageName) throws ExecutionException, InterruptedException { ArrayMap<String, ArraySet<String>> packageToFunctionIdMap = ArrayMap<String, ArraySet<String>> packageToFunctionIdMap; try { packageToFunctionIdMap = getPackageToFunctionIdMap( searchSession, schemaType, propertyFunctionId, propertyPackageName, DEFAULT_RESULT_COUNT_PER_PAGE); } catch (ExecutionException e) { // TODO: b/416177384 - Use AppSearchResult#RESULT_ABORTED instead of 13. if (!(e.getCause() instanceof AppSearchException) || (((AppSearchException) e.getCause()).getResultCode() != 13)) { throw e; } Slog.d( TAG, "Retrying to fetch app functions because AppSearch resulted in RESULT_ABORTED", e.getCause()); packageToFunctionIdMap = getPackageToFunctionIdMap( searchSession, schemaType, propertyFunctionId, propertyPackageName, DEFAULT_RESULT_COUNT_PER_PAGE); } // Since older mainline versions won't throw an exception we rely on checking if results // returned are same as DEFAULT_RESULT_COUNT_PER_PAGE. int functionIdCount = countTotalStringsInValueSets(packageToFunctionIdMap); if (functionIdCount == DEFAULT_RESULT_COUNT_PER_PAGE) { // We might run into b/400670498 where only the first page is returned while there Loading services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt +33 −0 Original line number Diff line number Diff line Loading @@ -35,10 +35,17 @@ import android.util.Log import androidx.test.platform.app.InstrumentationRegistry import com.android.internal.infra.AndroidFuture import com.google.common.truth.Truth.assertThat import org.mockito.kotlin.mock import java.util.concurrent.atomic.AtomicBoolean import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.mockito.kotlin.any import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @RunWith(JUnit4::class) class MetadataSyncAdapterTest { Loading Loading @@ -295,6 +302,32 @@ class MetadataSyncAdapterTest { assertThat(removedFunctionsDiffMap.isEmpty()).isEqualTo(true) } @Test fun getPackageToFunctionIdMapWithRetry_result_aborted_retries() { val futureSearchSession = mock<FutureAppSearchSession>() val futureSearchResults = mock<FutureSearchResults> { on { nextPage } doReturn AndroidFuture.completedFuture(listOf()) on { close() } doAnswer {} } whenever(futureSearchSession.search(any(), any())) .thenReturn( AndroidFuture<FutureSearchResults?>().apply { completeExceptionally(AppSearchException(/* resultCode= */ 13, "")) } ) .thenReturn(AndroidFuture.completedFuture(futureSearchResults)) MetadataSyncAdapter.getPackageToFunctionIdMapWithRetry( futureSearchSession, /* schemaType= */ "fakeSchema", AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID, AppFunctionRuntimeMetadata.PROPERTY_PACKAGE_NAME, ) verify(futureSearchSession, times(2)).search(any(), any()) } private companion object { const val TEST_DB: String = "test_db" const val TEST_TARGET_PKG_NAME = "com.android.frameworks.appfunctionstests" Loading Loading
services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java +42 −7 Original line number Diff line number Diff line Loading @@ -414,6 +414,16 @@ public class MetadataSyncAdapter { * This method returns a map of package names to a set of function ids from the AppFunction * metadata. * * <p>Retry Conditions: * * <ul> * <li>If an {@link AppSearchException} with {@code RESULT_ABORTED} (code 13) is thrown during * the first attempt, the query will be retried once. * <li>If the number of function ids returned equals {@code DEFAULT_RESULT_COUNT_PER_PAGE}, * which may indicate an incomplete result due to known issue b/400670498, the query will * be retried with an increased page size ({@code RETRY_RESULT_COUNT_PER_PAGE}). * </ul> * * @param searchSession The {@link FutureAppSearchSession} to search the AppFunction metadata. * @param schemaType The schema type of the AppFunction metadata. * @param propertyFunctionId The property name of the function id in the AppFunction metadata. Loading @@ -429,13 +439,38 @@ public class MetadataSyncAdapter { @NonNull String propertyFunctionId, @NonNull String propertyPackageName) throws ExecutionException, InterruptedException { ArrayMap<String, ArraySet<String>> packageToFunctionIdMap = ArrayMap<String, ArraySet<String>> packageToFunctionIdMap; try { packageToFunctionIdMap = getPackageToFunctionIdMap( searchSession, schemaType, propertyFunctionId, propertyPackageName, DEFAULT_RESULT_COUNT_PER_PAGE); } catch (ExecutionException e) { // TODO: b/416177384 - Use AppSearchResult#RESULT_ABORTED instead of 13. if (!(e.getCause() instanceof AppSearchException) || (((AppSearchException) e.getCause()).getResultCode() != 13)) { throw e; } Slog.d( TAG, "Retrying to fetch app functions because AppSearch resulted in RESULT_ABORTED", e.getCause()); packageToFunctionIdMap = getPackageToFunctionIdMap( searchSession, schemaType, propertyFunctionId, propertyPackageName, DEFAULT_RESULT_COUNT_PER_PAGE); } // Since older mainline versions won't throw an exception we rely on checking if results // returned are same as DEFAULT_RESULT_COUNT_PER_PAGE. int functionIdCount = countTotalStringsInValueSets(packageToFunctionIdMap); if (functionIdCount == DEFAULT_RESULT_COUNT_PER_PAGE) { // We might run into b/400670498 where only the first page is returned while there Loading
services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt +33 −0 Original line number Diff line number Diff line Loading @@ -35,10 +35,17 @@ import android.util.Log import androidx.test.platform.app.InstrumentationRegistry import com.android.internal.infra.AndroidFuture import com.google.common.truth.Truth.assertThat import org.mockito.kotlin.mock import java.util.concurrent.atomic.AtomicBoolean import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.mockito.kotlin.any import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @RunWith(JUnit4::class) class MetadataSyncAdapterTest { Loading Loading @@ -295,6 +302,32 @@ class MetadataSyncAdapterTest { assertThat(removedFunctionsDiffMap.isEmpty()).isEqualTo(true) } @Test fun getPackageToFunctionIdMapWithRetry_result_aborted_retries() { val futureSearchSession = mock<FutureAppSearchSession>() val futureSearchResults = mock<FutureSearchResults> { on { nextPage } doReturn AndroidFuture.completedFuture(listOf()) on { close() } doAnswer {} } whenever(futureSearchSession.search(any(), any())) .thenReturn( AndroidFuture<FutureSearchResults?>().apply { completeExceptionally(AppSearchException(/* resultCode= */ 13, "")) } ) .thenReturn(AndroidFuture.completedFuture(futureSearchResults)) MetadataSyncAdapter.getPackageToFunctionIdMapWithRetry( futureSearchSession, /* schemaType= */ "fakeSchema", AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID, AppFunctionRuntimeMetadata.PROPERTY_PACKAGE_NAME, ) verify(futureSearchSession, times(2)).search(any(), any()) } private companion object { const val TEST_DB: String = "test_db" const val TEST_TARGET_PKG_NAME = "com.android.frameworks.appfunctionstests" Loading