Loading apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +129 −125 Original line number Diff line number Diff line Loading @@ -32,13 +32,11 @@ import com.android.internal.infra.AndroidFuture; import com.android.internal.util.Preconditions; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.function.Consumer; Loading Loading @@ -158,68 +156,16 @@ public final class AppSearchSession implements Closeable { schemasPackageAccessibleBundles, callbackExecutor, callback); return; } try { // Migration process // 1. Generate the current and the final version map. // TODO(b/182855402) Release binder thread and move the heavy work into worker thread. AndroidFuture<AppSearchResult<GetSchemaResponse>> future = new AndroidFuture<>(); getSchema(callbackExecutor, future::complete); AppSearchResult<GetSchemaResponse> getSchemaResult = future.get(); if (!getSchemaResult.isSuccess()) { callback.accept(AppSearchResult.newFailedResult(getSchemaResult)); return; } GetSchemaResponse getSchemaResponse = getSchemaResult.getResultValue(); Set<AppSearchSchema> currentSchemas = getSchemaResponse.getSchemas(); Map<String, Integer> currentVersionMap = SchemaMigrationUtil.buildVersionMap(currentSchemas, getSchemaResponse.getVersion()); Map<String, Integer> finalVersionMap = SchemaMigrationUtil.buildVersionMap(request.getSchemas(), request.getVersion()); // 2. SetSchema with forceOverride=false, to retrieve the list of incompatible/deleted // types. mService.setSchema( mPackageName, mDatabaseName, } else { setSchemaWithMigrations( request, schemaBundles, new ArrayList<>(request.getSchemasNotDisplayedBySystem()), schemasPackageAccessibleBundles, /*forceOverride=*/ false, mUserId, request.getVersion(), new IAppSearchResultCallback.Stub() { public void onResult(AppSearchResult result) { callbackExecutor.execute(() -> { if (result.isSuccess()) { // TODO(b/183177268): once migration is implemented, run // it on workExecutor. try { Bundle bundle = (Bundle) result.getResultValue(); SetSchemaResponse setSchemaResponse = new SetSchemaResponse(bundle); setSchemaMigration( request, setSchemaResponse, schemaBundles, schemasPackageAccessibleBundles, currentVersionMap, finalVersionMap, callback); } catch (Throwable t) { callback.accept(AppSearchResult.throwableToFailedResult(t)); } } else { callback.accept(result); } }); workExecutor, callbackExecutor, callback); } }); mIsMutated = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (Throwable t) { callback.accept(AppSearchResult.throwableToFailedResult(t)); } } /** Loading Loading @@ -667,7 +613,8 @@ public final class AppSearchSession implements Closeable { * <p>We only need one time {@link #setSchema} call for no-migration scenario by using the * forceoverride in the request. */ private void setSchemaNoMigrations(@NonNull SetSchemaRequest request, private void setSchemaNoMigrations( @NonNull SetSchemaRequest request, @NonNull List<Bundle> schemaBundles, @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles, @NonNull @CallbackExecutor Executor executor, Loading Loading @@ -709,7 +656,6 @@ public final class AppSearchSession implements Closeable { }); } }); mIsMutated = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading @@ -721,41 +667,88 @@ public final class AppSearchSession implements Closeable { * <p>First time {@link #setSchema} call with forceOverride is false gives us all incompatible * changes. After trigger migrations, the second time call {@link #setSchema} will actually * apply the changes. * * @param setSchemaResponse the result of the first setSchema call with forceOverride=false. */ private void setSchemaMigration(@NonNull SetSchemaRequest request, @NonNull SetSchemaResponse setSchemaResponse, private void setSchemaWithMigrations( @NonNull SetSchemaRequest request, @NonNull List<Bundle> schemaBundles, @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles, @NonNull Map<String, Integer> currentVersionMap, Map<String, Integer> finalVersionMap, @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) throws AppSearchException, IOException, RemoteException, ExecutionException, InterruptedException { @NonNull Executor workExecutor, @NonNull @CallbackExecutor Executor callbackExecutor, @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) { workExecutor.execute(() -> { try { // Migration process // 1. Generate the current and the final version map. AndroidFuture<AppSearchResult<GetSchemaResponse>> getSchemaFuture = new AndroidFuture<>(); getSchema(callbackExecutor, getSchemaFuture::complete); AppSearchResult<GetSchemaResponse> getSchemaResult = getSchemaFuture.get(); if (!getSchemaResult.isSuccess()) { callbackExecutor.execute(() -> callback.accept(AppSearchResult.newFailedResult(getSchemaResult))); return; } GetSchemaResponse getSchemaResponse = getSchemaResult.getResultValue(); Set<AppSearchSchema> currentSchemas = getSchemaResponse.getSchemas(); Map<String, Integer> currentVersionMap = SchemaMigrationUtil.buildVersionMap( currentSchemas, getSchemaResponse.getVersion()); Map<String, Integer> finalVersionMap = SchemaMigrationUtil.buildVersionMap( request.getSchemas(), request.getVersion()); // 2. SetSchema with forceOverride=false, to retrieve the list of // incompatible/deleted types. AndroidFuture<AppSearchResult<Bundle>> setSchemaFuture = new AndroidFuture<>(); mService.setSchema( mPackageName, mDatabaseName, schemaBundles, new ArrayList<>(request.getSchemasNotDisplayedBySystem()), schemasPackageAccessibleBundles, /*forceOverride=*/ false, mUserId, request.getVersion(), new IAppSearchResultCallback.Stub() { public void onResult(AppSearchResult result) { setSchemaFuture.complete(result); } }); AppSearchResult<Bundle> setSchemaResult = setSchemaFuture.get(); if (!setSchemaResult.isSuccess()) { callbackExecutor.execute(() -> callback.accept(AppSearchResult.newFailedResult(setSchemaResult))); return; } SetSchemaResponse setSchemaResponse = new SetSchemaResponse(setSchemaResult.getResultValue()); // 1. If forceOverride is false, check that all incompatible types will be migrated. // If some aren't we must throw an error, rather than proceeding and deleting those // types. if (!request.isForceOverride()) { Set<String> unmigratedTypes = SchemaMigrationUtil.getUnmigratedIncompatibleTypes( Set<String> unmigratedTypes = SchemaMigrationUtil.getUnmigratedIncompatibleTypes( setSchemaResponse.getIncompatibleTypes(), request.getMigrators(), currentVersionMap, finalVersionMap); // check if there are any unmigrated types or deleted types. If there are, we will throw // an exception. // Since the force override is false, the schema will not have been set if there are any // incompatible or deleted types. checkDeletedAndIncompatible(setSchemaResponse.getDeletedTypes(), unmigratedTypes); // check if there are any unmigrated types or deleted types. If there are, we // will throw an exception. // Since the force override is false, the schema will not have been set if there // are any incompatible or deleted types. checkDeletedAndIncompatible( setSchemaResponse.getDeletedTypes(), unmigratedTypes); } try (AppSearchMigrationHelper migrationHelper = new AppSearchMigrationHelper(mService, mUserId, currentVersionMap, finalVersionMap, mPackageName, mDatabaseName)) { new AppSearchMigrationHelper( mService, mUserId, currentVersionMap, finalVersionMap, mPackageName, mDatabaseName)) { Map<String, Migrator> migratorMap = request.getMigrators(); // 2. Trigger migration for all migrators. // TODO(b/177266929) trigger migration for all types together rather than separately. // TODO(b/177266929) trigger migration for all types together rather than // separately. Set<String> migratedTypes = new ArraySet<>(); for (Map.Entry<String, Migrator> entry : migratorMap.entrySet()) { String schemaType = entry.getKey(); Loading @@ -767,10 +760,12 @@ public final class AppSearchSession implements Closeable { } } // 3. SetSchema a second time with forceOverride=true if the first attempted failed. // 3. SetSchema a second time with forceOverride=true if the first attempted // failed. if (!setSchemaResponse.getIncompatibleTypes().isEmpty() || !setSchemaResponse.getDeletedTypes().isEmpty()) { AndroidFuture<AppSearchResult<SetSchemaResponse>> future = new AndroidFuture<>(); AndroidFuture<AppSearchResult<Bundle>> setSchema2Future = new AndroidFuture<>(); // only trigger second setSchema() call if the first one is fail. mService.setSchema( mPackageName, Loading @@ -779,28 +774,37 @@ public final class AppSearchSession implements Closeable { new ArrayList<>(request.getSchemasNotDisplayedBySystem()), schemasPackageAccessibleBundles, /*forceOverride=*/ true, mUserId, request.getVersion(), mUserId, new IAppSearchResultCallback.Stub() { @Override public void onResult(AppSearchResult result) throws RemoteException { future.complete(result); public void onResult(AppSearchResult result) { setSchema2Future.complete(result); } }); AppSearchResult<SetSchemaResponse> secondSetSchemaResult = future.get(); if (!secondSetSchemaResult.isSuccess()) { // we failed to set the schema in second time with force override = true, which // is an impossible case. Since we only swallow the incompatible error in the // first setSchema call, all other errors will be thrown at the first time. callback.accept(secondSetSchemaResult); AppSearchResult<Bundle> setSchema2Result = setSchema2Future.get(); if (!setSchema2Result.isSuccess()) { // we failed to set the schema in second time with forceOverride = true, // which is an impossible case. Since we only swallow the incompatible // error in the first setSchema call, all other errors will be thrown at // the first time. callbackExecutor.execute(() -> callback.accept( AppSearchResult.newFailedResult(setSchemaResult))); return; } } SetSchemaResponse.Builder responseBuilder = setSchemaResponse.toBuilder() .addMigratedTypes(migratedTypes); callback.accept(migrationHelper.putMigratedDocuments(responseBuilder)); AppSearchResult<SetSchemaResponse> putResult = migrationHelper.putMigratedDocuments(responseBuilder); callbackExecutor.execute(() -> callback.accept(putResult)); } } catch (Throwable t) { callbackExecutor.execute(() -> callback.accept( AppSearchResult.throwableToFailedResult(t))); } }); } /** Checks the setSchema() call won't delete any types or has incompatible types. */ Loading Loading
apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +129 −125 Original line number Diff line number Diff line Loading @@ -32,13 +32,11 @@ import com.android.internal.infra.AndroidFuture; import com.android.internal.util.Preconditions; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.function.Consumer; Loading Loading @@ -158,68 +156,16 @@ public final class AppSearchSession implements Closeable { schemasPackageAccessibleBundles, callbackExecutor, callback); return; } try { // Migration process // 1. Generate the current and the final version map. // TODO(b/182855402) Release binder thread and move the heavy work into worker thread. AndroidFuture<AppSearchResult<GetSchemaResponse>> future = new AndroidFuture<>(); getSchema(callbackExecutor, future::complete); AppSearchResult<GetSchemaResponse> getSchemaResult = future.get(); if (!getSchemaResult.isSuccess()) { callback.accept(AppSearchResult.newFailedResult(getSchemaResult)); return; } GetSchemaResponse getSchemaResponse = getSchemaResult.getResultValue(); Set<AppSearchSchema> currentSchemas = getSchemaResponse.getSchemas(); Map<String, Integer> currentVersionMap = SchemaMigrationUtil.buildVersionMap(currentSchemas, getSchemaResponse.getVersion()); Map<String, Integer> finalVersionMap = SchemaMigrationUtil.buildVersionMap(request.getSchemas(), request.getVersion()); // 2. SetSchema with forceOverride=false, to retrieve the list of incompatible/deleted // types. mService.setSchema( mPackageName, mDatabaseName, } else { setSchemaWithMigrations( request, schemaBundles, new ArrayList<>(request.getSchemasNotDisplayedBySystem()), schemasPackageAccessibleBundles, /*forceOverride=*/ false, mUserId, request.getVersion(), new IAppSearchResultCallback.Stub() { public void onResult(AppSearchResult result) { callbackExecutor.execute(() -> { if (result.isSuccess()) { // TODO(b/183177268): once migration is implemented, run // it on workExecutor. try { Bundle bundle = (Bundle) result.getResultValue(); SetSchemaResponse setSchemaResponse = new SetSchemaResponse(bundle); setSchemaMigration( request, setSchemaResponse, schemaBundles, schemasPackageAccessibleBundles, currentVersionMap, finalVersionMap, callback); } catch (Throwable t) { callback.accept(AppSearchResult.throwableToFailedResult(t)); } } else { callback.accept(result); } }); workExecutor, callbackExecutor, callback); } }); mIsMutated = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (Throwable t) { callback.accept(AppSearchResult.throwableToFailedResult(t)); } } /** Loading Loading @@ -667,7 +613,8 @@ public final class AppSearchSession implements Closeable { * <p>We only need one time {@link #setSchema} call for no-migration scenario by using the * forceoverride in the request. */ private void setSchemaNoMigrations(@NonNull SetSchemaRequest request, private void setSchemaNoMigrations( @NonNull SetSchemaRequest request, @NonNull List<Bundle> schemaBundles, @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles, @NonNull @CallbackExecutor Executor executor, Loading Loading @@ -709,7 +656,6 @@ public final class AppSearchSession implements Closeable { }); } }); mIsMutated = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading @@ -721,41 +667,88 @@ public final class AppSearchSession implements Closeable { * <p>First time {@link #setSchema} call with forceOverride is false gives us all incompatible * changes. After trigger migrations, the second time call {@link #setSchema} will actually * apply the changes. * * @param setSchemaResponse the result of the first setSchema call with forceOverride=false. */ private void setSchemaMigration(@NonNull SetSchemaRequest request, @NonNull SetSchemaResponse setSchemaResponse, private void setSchemaWithMigrations( @NonNull SetSchemaRequest request, @NonNull List<Bundle> schemaBundles, @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles, @NonNull Map<String, Integer> currentVersionMap, Map<String, Integer> finalVersionMap, @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) throws AppSearchException, IOException, RemoteException, ExecutionException, InterruptedException { @NonNull Executor workExecutor, @NonNull @CallbackExecutor Executor callbackExecutor, @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) { workExecutor.execute(() -> { try { // Migration process // 1. Generate the current and the final version map. AndroidFuture<AppSearchResult<GetSchemaResponse>> getSchemaFuture = new AndroidFuture<>(); getSchema(callbackExecutor, getSchemaFuture::complete); AppSearchResult<GetSchemaResponse> getSchemaResult = getSchemaFuture.get(); if (!getSchemaResult.isSuccess()) { callbackExecutor.execute(() -> callback.accept(AppSearchResult.newFailedResult(getSchemaResult))); return; } GetSchemaResponse getSchemaResponse = getSchemaResult.getResultValue(); Set<AppSearchSchema> currentSchemas = getSchemaResponse.getSchemas(); Map<String, Integer> currentVersionMap = SchemaMigrationUtil.buildVersionMap( currentSchemas, getSchemaResponse.getVersion()); Map<String, Integer> finalVersionMap = SchemaMigrationUtil.buildVersionMap( request.getSchemas(), request.getVersion()); // 2. SetSchema with forceOverride=false, to retrieve the list of // incompatible/deleted types. AndroidFuture<AppSearchResult<Bundle>> setSchemaFuture = new AndroidFuture<>(); mService.setSchema( mPackageName, mDatabaseName, schemaBundles, new ArrayList<>(request.getSchemasNotDisplayedBySystem()), schemasPackageAccessibleBundles, /*forceOverride=*/ false, mUserId, request.getVersion(), new IAppSearchResultCallback.Stub() { public void onResult(AppSearchResult result) { setSchemaFuture.complete(result); } }); AppSearchResult<Bundle> setSchemaResult = setSchemaFuture.get(); if (!setSchemaResult.isSuccess()) { callbackExecutor.execute(() -> callback.accept(AppSearchResult.newFailedResult(setSchemaResult))); return; } SetSchemaResponse setSchemaResponse = new SetSchemaResponse(setSchemaResult.getResultValue()); // 1. If forceOverride is false, check that all incompatible types will be migrated. // If some aren't we must throw an error, rather than proceeding and deleting those // types. if (!request.isForceOverride()) { Set<String> unmigratedTypes = SchemaMigrationUtil.getUnmigratedIncompatibleTypes( Set<String> unmigratedTypes = SchemaMigrationUtil.getUnmigratedIncompatibleTypes( setSchemaResponse.getIncompatibleTypes(), request.getMigrators(), currentVersionMap, finalVersionMap); // check if there are any unmigrated types or deleted types. If there are, we will throw // an exception. // Since the force override is false, the schema will not have been set if there are any // incompatible or deleted types. checkDeletedAndIncompatible(setSchemaResponse.getDeletedTypes(), unmigratedTypes); // check if there are any unmigrated types or deleted types. If there are, we // will throw an exception. // Since the force override is false, the schema will not have been set if there // are any incompatible or deleted types. checkDeletedAndIncompatible( setSchemaResponse.getDeletedTypes(), unmigratedTypes); } try (AppSearchMigrationHelper migrationHelper = new AppSearchMigrationHelper(mService, mUserId, currentVersionMap, finalVersionMap, mPackageName, mDatabaseName)) { new AppSearchMigrationHelper( mService, mUserId, currentVersionMap, finalVersionMap, mPackageName, mDatabaseName)) { Map<String, Migrator> migratorMap = request.getMigrators(); // 2. Trigger migration for all migrators. // TODO(b/177266929) trigger migration for all types together rather than separately. // TODO(b/177266929) trigger migration for all types together rather than // separately. Set<String> migratedTypes = new ArraySet<>(); for (Map.Entry<String, Migrator> entry : migratorMap.entrySet()) { String schemaType = entry.getKey(); Loading @@ -767,10 +760,12 @@ public final class AppSearchSession implements Closeable { } } // 3. SetSchema a second time with forceOverride=true if the first attempted failed. // 3. SetSchema a second time with forceOverride=true if the first attempted // failed. if (!setSchemaResponse.getIncompatibleTypes().isEmpty() || !setSchemaResponse.getDeletedTypes().isEmpty()) { AndroidFuture<AppSearchResult<SetSchemaResponse>> future = new AndroidFuture<>(); AndroidFuture<AppSearchResult<Bundle>> setSchema2Future = new AndroidFuture<>(); // only trigger second setSchema() call if the first one is fail. mService.setSchema( mPackageName, Loading @@ -779,28 +774,37 @@ public final class AppSearchSession implements Closeable { new ArrayList<>(request.getSchemasNotDisplayedBySystem()), schemasPackageAccessibleBundles, /*forceOverride=*/ true, mUserId, request.getVersion(), mUserId, new IAppSearchResultCallback.Stub() { @Override public void onResult(AppSearchResult result) throws RemoteException { future.complete(result); public void onResult(AppSearchResult result) { setSchema2Future.complete(result); } }); AppSearchResult<SetSchemaResponse> secondSetSchemaResult = future.get(); if (!secondSetSchemaResult.isSuccess()) { // we failed to set the schema in second time with force override = true, which // is an impossible case. Since we only swallow the incompatible error in the // first setSchema call, all other errors will be thrown at the first time. callback.accept(secondSetSchemaResult); AppSearchResult<Bundle> setSchema2Result = setSchema2Future.get(); if (!setSchema2Result.isSuccess()) { // we failed to set the schema in second time with forceOverride = true, // which is an impossible case. Since we only swallow the incompatible // error in the first setSchema call, all other errors will be thrown at // the first time. callbackExecutor.execute(() -> callback.accept( AppSearchResult.newFailedResult(setSchemaResult))); return; } } SetSchemaResponse.Builder responseBuilder = setSchemaResponse.toBuilder() .addMigratedTypes(migratedTypes); callback.accept(migrationHelper.putMigratedDocuments(responseBuilder)); AppSearchResult<SetSchemaResponse> putResult = migrationHelper.putMigratedDocuments(responseBuilder); callbackExecutor.execute(() -> callback.accept(putResult)); } } catch (Throwable t) { callbackExecutor.execute(() -> callback.accept( AppSearchResult.throwableToFailedResult(t))); } }); } /** Checks the setSchema() call won't delete any types or has incompatible types. */ Loading