Merge "Implement schema migration to another type in framework." into sc-dev

This commit is contained in:
Terry Wang
2021-04-08 02:36:51 +00:00
committed by Android (Google) Code Review
5 changed files with 115 additions and 139 deletions

View File

@@ -16,6 +16,7 @@
package android.app.appsearch; package android.app.appsearch;
import static android.app.appsearch.AppSearchResult.RESULT_INVALID_SCHEMA;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY; import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
@@ -27,6 +28,7 @@ import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.ArraySet;
import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.AndroidFuture;
@@ -39,8 +41,8 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
/** /**
@@ -55,23 +57,23 @@ public class AppSearchMigrationHelper implements Closeable {
private final String mDatabaseName; private final String mDatabaseName;
private final int mUserId; private final int mUserId;
private final File mMigratedFile; private final File mMigratedFile;
private final Map<String, Integer> mCurrentVersionMap; private final Set<String> mDestinationTypes;
private final Map<String, Integer> mFinalVersionMap;
private boolean mAreDocumentsMigrated = false; private boolean mAreDocumentsMigrated = false;
AppSearchMigrationHelper(@NonNull IAppSearchManager service, AppSearchMigrationHelper(@NonNull IAppSearchManager service,
@UserIdInt int userId, @UserIdInt int userId,
@NonNull Map<String, Integer> currentVersionMap,
@NonNull Map<String, Integer> finalVersionMap,
@NonNull String packageName, @NonNull String packageName,
@NonNull String databaseName) throws IOException { @NonNull String databaseName,
@NonNull Set<AppSearchSchema> newSchemas) throws IOException {
mService = Objects.requireNonNull(service); mService = Objects.requireNonNull(service);
mCurrentVersionMap = Objects.requireNonNull(currentVersionMap);
mFinalVersionMap = Objects.requireNonNull(finalVersionMap);
mPackageName = Objects.requireNonNull(packageName); mPackageName = Objects.requireNonNull(packageName);
mDatabaseName = Objects.requireNonNull(databaseName); mDatabaseName = Objects.requireNonNull(databaseName);
mUserId = userId; mUserId = userId;
mMigratedFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null); mMigratedFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null);
mDestinationTypes = new ArraySet<>(newSchemas.size());
for (AppSearchSchema newSchema : newSchemas) {
mDestinationTypes.add(newSchema.getSchemaType());
}
} }
/** /**
@@ -87,7 +89,8 @@ public class AppSearchMigrationHelper implements Closeable {
* GenericDocument} to new version. * GenericDocument} to new version.
*/ */
@WorkerThread @WorkerThread
public void queryAndTransform(@NonNull String schemaType, @NonNull Migrator migrator) public void queryAndTransform(@NonNull String schemaType, @NonNull Migrator migrator,
int currentVersion, int finalVersion)
throws IOException, AppSearchException, InterruptedException, ExecutionException { throws IOException, AppSearchException, InterruptedException, ExecutionException {
File queryFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null); File queryFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null);
try (ParcelFileDescriptor fileDescriptor = try (ParcelFileDescriptor fileDescriptor =
@@ -111,7 +114,7 @@ public class AppSearchMigrationHelper implements Closeable {
if (!result.isSuccess()) { if (!result.isSuccess()) {
throw new AppSearchException(result.getResultCode(), result.getErrorMessage()); throw new AppSearchException(result.getResultCode(), result.getErrorMessage());
} }
readAndTransform(queryFile, migrator); readAndTransform(queryFile, migrator, currentVersion, finalVersion);
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} finally { } finally {
@@ -173,8 +176,9 @@ public class AppSearchMigrationHelper implements Closeable {
* *
* <p>Save migrated {@link GenericDocument}s to the {@link #mMigratedFile}. * <p>Save migrated {@link GenericDocument}s to the {@link #mMigratedFile}.
*/ */
private void readAndTransform(@NonNull File file, @NonNull Migrator migrator) private void readAndTransform(@NonNull File file, @NonNull Migrator migrator,
throws IOException { int currentVersion, int finalVersion)
throws IOException, AppSearchException {
try (DataInputStream inputStream = new DataInputStream(new FileInputStream(file)); try (DataInputStream inputStream = new DataInputStream(new FileInputStream(file));
DataOutputStream outputStream = new DataOutputStream(new FileOutputStream( DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(
mMigratedFile, /*append=*/ true))) { mMigratedFile, /*append=*/ true))) {
@@ -187,9 +191,6 @@ public class AppSearchMigrationHelper implements Closeable {
// Nothing wrong. We just finished reading. // Nothing wrong. We just finished reading.
} }
int currentVersion = mCurrentVersionMap.get(document.getSchemaType());
int finalVersion = mFinalVersionMap.get(document.getSchemaType());
GenericDocument newDocument; GenericDocument newDocument;
if (currentVersion < finalVersion) { if (currentVersion < finalVersion) {
newDocument = migrator.onUpgrade(currentVersion, finalVersion, document); newDocument = migrator.onUpgrade(currentVersion, finalVersion, document);
@@ -197,6 +198,18 @@ public class AppSearchMigrationHelper implements Closeable {
// currentVersion == finalVersion case won't trigger migration and get here. // currentVersion == finalVersion case won't trigger migration and get here.
newDocument = migrator.onDowngrade(currentVersion, finalVersion, document); newDocument = migrator.onDowngrade(currentVersion, finalVersion, document);
} }
if (!mDestinationTypes.contains(newDocument.getSchemaType())) {
// we exit before the new schema has been set to AppSearch. So no
// observable changes will be applied to stored schemas and documents.
// And the temp file will be deleted at close(), which will be triggered at
// the end of try-with-resources block of SearchSessionImpl.
throw new AppSearchException(
RESULT_INVALID_SCHEMA,
"Receive a migrated document with schema type: "
+ newDocument.getSchemaType()
+ ". But the schema types doesn't exist in the request");
}
writeBundleToOutputStream(outputStream, newDocument.getBundle()); writeBundleToOutputStream(outputStream, newDocument.getBundle());
} }
mAreDocumentsMigrated = true; mAreDocumentsMigrated = true;

View File

@@ -19,7 +19,6 @@ package android.app.appsearch;
import android.annotation.CallbackExecutor; import android.annotation.CallbackExecutor;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.app.appsearch.exceptions.AppSearchException;
import android.app.appsearch.util.SchemaMigrationUtil; import android.app.appsearch.util.SchemaMigrationUtil;
import android.os.Bundle; import android.os.Bundle;
import android.os.ParcelableException; import android.os.ParcelableException;
@@ -647,8 +646,8 @@ public final class AppSearchSession implements Closeable {
new ArrayList<>(request.getSchemasNotDisplayedBySystem()), new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
schemasPackageAccessibleBundles, schemasPackageAccessibleBundles,
request.isForceOverride(), request.isForceOverride(),
mUserId,
request.getVersion(), request.getVersion(),
mUserId,
new IAppSearchResultCallback.Stub() { new IAppSearchResultCallback.Stub() {
public void onResult(AppSearchResult result) { public void onResult(AppSearchResult result) {
executor.execute(() -> { executor.execute(() -> {
@@ -661,7 +660,7 @@ public final class AppSearchSession implements Closeable {
// Throw exception if there is any deleted types or // Throw exception if there is any deleted types or
// incompatible types. That's the only case we swallowed // incompatible types. That's the only case we swallowed
// in the AppSearchImpl#setSchema(). // in the AppSearchImpl#setSchema().
checkDeletedAndIncompatible( SchemaMigrationUtil.checkDeletedAndIncompatible(
setSchemaResponse.getDeletedTypes(), setSchemaResponse.getDeletedTypes(),
setSchemaResponse.getIncompatibleTypes()); setSchemaResponse.getIncompatibleTypes());
} }
@@ -698,7 +697,7 @@ public final class AppSearchSession implements Closeable {
workExecutor.execute(() -> { workExecutor.execute(() -> {
try { try {
// Migration process // Migration process
// 1. Generate the current and the final version map. // 1. Validate and retrieve all active migrators.
AndroidFuture<AppSearchResult<GetSchemaResponse>> getSchemaFuture = AndroidFuture<AppSearchResult<GetSchemaResponse>> getSchemaFuture =
new AndroidFuture<>(); new AndroidFuture<>();
getSchema(callbackExecutor, getSchemaFuture::complete); getSchema(callbackExecutor, getSchemaFuture::complete);
@@ -709,11 +708,18 @@ public final class AppSearchSession implements Closeable {
return; return;
} }
GetSchemaResponse getSchemaResponse = getSchemaResult.getResultValue(); GetSchemaResponse getSchemaResponse = getSchemaResult.getResultValue();
Set<AppSearchSchema> currentSchemas = getSchemaResponse.getSchemas(); int currentVersion = getSchemaResponse.getVersion();
Map<String, Integer> currentVersionMap = SchemaMigrationUtil.buildVersionMap( int finalVersion = request.getVersion();
currentSchemas, getSchemaResponse.getVersion()); Map<String, Migrator> activeMigrators = SchemaMigrationUtil.getActiveMigrators(
Map<String, Integer> finalVersionMap = SchemaMigrationUtil.buildVersionMap( getSchemaResponse.getSchemas(), request.getMigrators(), currentVersion,
request.getSchemas(), request.getVersion()); finalVersion);
// No need to trigger migration if no migrator is active.
if (activeMigrators.isEmpty()) {
setSchemaNoMigrations(request, schemaBundles, schemasPackageAccessibleBundles,
callbackExecutor, callback);
return;
}
// 2. SetSchema with forceOverride=false, to retrieve the list of // 2. SetSchema with forceOverride=false, to retrieve the list of
// incompatible/deleted types. // incompatible/deleted types.
@@ -725,8 +731,8 @@ public final class AppSearchSession implements Closeable {
new ArrayList<>(request.getSchemasNotDisplayedBySystem()), new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
schemasPackageAccessibleBundles, schemasPackageAccessibleBundles,
/*forceOverride=*/ false, /*forceOverride=*/ false,
mUserId,
request.getVersion(), request.getVersion(),
mUserId,
new IAppSearchResultCallback.Stub() { new IAppSearchResultCallback.Stub() {
public void onResult(AppSearchResult result) { public void onResult(AppSearchResult result) {
setSchemaFuture.complete(result); setSchemaFuture.complete(result);
@@ -741,46 +747,27 @@ public final class AppSearchSession implements Closeable {
SetSchemaResponse setSchemaResponse = SetSchemaResponse setSchemaResponse =
new SetSchemaResponse(setSchemaResult.getResultValue()); new SetSchemaResponse(setSchemaResult.getResultValue());
// 1. If forceOverride is false, check that all incompatible types will be migrated. // 3. 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 // If some aren't we must throw an error, rather than proceeding and deleting those
// types. // types.
if (!request.isForceOverride()) { if (!request.isForceOverride()) {
Set<String> unmigratedTypes = SchemaMigrationUtil.checkDeletedAndIncompatibleAfterMigration(setSchemaResponse,
SchemaMigrationUtil.getUnmigratedIncompatibleTypes( activeMigrators.keySet());
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);
} }
try (AppSearchMigrationHelper migrationHelper = try (AppSearchMigrationHelper migrationHelper = new AppSearchMigrationHelper(
new AppSearchMigrationHelper( mService, mUserId, mPackageName, mDatabaseName, request.getSchemas())) {
mService, mUserId, currentVersionMap, finalVersionMap,
mPackageName, mDatabaseName)) {
Map<String, Migrator> migratorMap = request.getMigrators();
// 2. Trigger migration for all migrators. // 4. Trigger migration for all migrators.
// TODO(b/177266929) trigger migration for all types together rather than // TODO(b/177266929) trigger migration for all types together rather than
// separately. // separately.
Set<String> migratedTypes = new ArraySet<>(); for (Map.Entry<String, Migrator> entry : activeMigrators.entrySet()) {
for (Map.Entry<String, Migrator> entry : migratorMap.entrySet()) { migrationHelper.queryAndTransform(/*schemaType=*/ entry.getKey(),
String schemaType = entry.getKey(); /*migrator=*/ entry.getValue(), currentVersion,
Migrator migrator = entry.getValue(); finalVersion);
if (SchemaMigrationUtil.shouldTriggerMigration(
schemaType, migrator, currentVersionMap, finalVersionMap)) {
migrationHelper.queryAndTransform(schemaType, migrator);
migratedTypes.add(schemaType);
}
} }
// 3. SetSchema a second time with forceOverride=true if the first attempted // 5. SetSchema a second time with forceOverride=true if the first attempted
// failed. // failed.
if (!setSchemaResponse.getIncompatibleTypes().isEmpty() if (!setSchemaResponse.getIncompatibleTypes().isEmpty()
|| !setSchemaResponse.getDeletedTypes().isEmpty()) { || !setSchemaResponse.getDeletedTypes().isEmpty()) {
@@ -809,13 +796,16 @@ public final class AppSearchSession implements Closeable {
// error in the first setSchema call, all other errors will be thrown at // error in the first setSchema call, all other errors will be thrown at
// the first time. // the first time.
callbackExecutor.execute(() -> callback.accept( callbackExecutor.execute(() -> callback.accept(
AppSearchResult.newFailedResult(setSchemaResult))); AppSearchResult.newFailedResult(setSchema2Result)));
return; return;
} }
} }
SetSchemaResponse.Builder responseBuilder = setSchemaResponse.toBuilder() SetSchemaResponse.Builder responseBuilder = setSchemaResponse.toBuilder()
.addMigratedTypes(migratedTypes); .addMigratedTypes(activeMigrators.keySet());
// 6. Put all the migrated documents into the index, now that the new schema is
// set.
AppSearchResult<SetSchemaResponse> putResult = AppSearchResult<SetSchemaResponse> putResult =
migrationHelper.putMigratedDocuments(responseBuilder); migrationHelper.putMigratedDocuments(responseBuilder);
callbackExecutor.execute(() -> callback.accept(putResult)); callbackExecutor.execute(() -> callback.accept(putResult));
@@ -826,17 +816,4 @@ public final class AppSearchSession implements Closeable {
} }
}); });
} }
/** Checks the setSchema() call won't delete any types or has incompatible types. */
//TODO(b/177266929) move this method to util
private void checkDeletedAndIncompatible(Set<String> deletedTypes,
Set<String> incompatibleTypes)
throws AppSearchException {
if (!deletedTypes.isEmpty() || !incompatibleTypes.isEmpty()) {
String newMessage = "Schema is incompatible."
+ "\n Deleted types: " + deletedTypes
+ "\n Incompatible types: " + incompatibleTypes;
throw new AppSearchException(AppSearchResult.RESULT_INVALID_SCHEMA, newMessage);
}
}
} }

View File

@@ -40,6 +40,7 @@ interface IAppSearchManager {
* packages. The value List contains PackageIdentifier Bundles. * packages. The value List contains PackageIdentifier Bundles.
* @param forceOverride Whether to apply the new schema even if it is incompatible. All * @param forceOverride Whether to apply the new schema even if it is incompatible. All
* incompatible documents will be deleted. * incompatible documents will be deleted.
* @param schemaVersion The overall schema version number of the request.
* @param userId Id of the calling user * @param userId Id of the calling user
* @param callback {@link IAppSearchResultCallback#onResult} will be called with an * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
* {@link AppSearchResult}&lt;{@link Bundle}&gt;, where the value are * {@link AppSearchResult}&lt;{@link Bundle}&gt;, where the value are
@@ -52,8 +53,8 @@ interface IAppSearchManager {
in List<String> schemasNotDisplayedBySystem, in List<String> schemasNotDisplayedBySystem,
in Map<String, List<Bundle>> schemasPackageAccessibleBundles, in Map<String, List<Bundle>> schemasPackageAccessibleBundles,
boolean forceOverride, boolean forceOverride,
in int userId,
in int schemaVersion, in int schemaVersion,
in int userId,
in IAppSearchResultCallback callback); in IAppSearchResultCallback callback);
/** /**

View File

@@ -20,12 +20,11 @@ import android.annotation.NonNull;
import android.app.appsearch.AppSearchResult; import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema; import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.Migrator; import android.app.appsearch.Migrator;
import android.app.appsearch.SetSchemaResponse;
import android.app.appsearch.exceptions.AppSearchException; import android.app.appsearch.exceptions.AppSearchException;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Log;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -36,84 +35,70 @@ import java.util.Set;
* @hide * @hide
*/ */
public final class SchemaMigrationUtil { public final class SchemaMigrationUtil {
private static final String TAG = "AppSearchMigrateUtil";
private SchemaMigrationUtil() {} private SchemaMigrationUtil() {}
/** /** Returns all active {@link Migrator}s that need to be triggered in this migration. */
* Finds out which incompatible schema type won't be migrated by comparing its current and final
* version number.
*/
@NonNull @NonNull
public static Set<String> getUnmigratedIncompatibleTypes( public static Map<String, Migrator> getActiveMigrators(
@NonNull Set<String> incompatibleSchemaTypes, @NonNull Set<AppSearchSchema> existingSchemas,
@NonNull Map<String, Migrator> migrators, @NonNull Map<String, Migrator> migrators,
@NonNull Map<String, Integer> currentVersionMap, int currentVersion,
@NonNull Map<String, Integer> finalVersionMap) int finalVersion) {
throws AppSearchException { if (currentVersion == finalVersion) {
Set<String> unmigratedSchemaTypes = new ArraySet<>(); return Collections.emptyMap();
for (String unmigratedSchemaType : incompatibleSchemaTypes) { }
Integer currentVersion = currentVersionMap.get(unmigratedSchemaType); Set<String> existingTypes = new ArraySet<>(existingSchemas.size());
Integer finalVersion = finalVersionMap.get(unmigratedSchemaType); for (AppSearchSchema schema : existingSchemas) {
if (currentVersion == null) { existingTypes.add(schema.getSchemaType());
// impossible, we have done something wrong. }
throw new AppSearchException(
AppSearchResult.RESULT_UNKNOWN_ERROR, Map<String, Migrator> activeMigrators = new ArrayMap<>();
"Cannot find the current version number for schema type: " for (Map.Entry<String, Migrator> entry : migrators.entrySet()) {
+ unmigratedSchemaType); // The device contains the source type, and we should trigger migration for the type.
} String schemaType = entry.getKey();
if (finalVersion == null) { Migrator migrator = entry.getValue();
// The schema doesn't exist in the SetSchemaRequest. if (existingTypes.contains(schemaType)
unmigratedSchemaTypes.add(unmigratedSchemaType); && migrator.shouldMigrate(currentVersion, finalVersion)) {
continue; activeMigrators.put(schemaType, migrator);
}
// we don't have migrator or won't trigger migration for this schema type.
Migrator migrator = migrators.get(unmigratedSchemaType);
if (migrator == null
|| !migrator.shouldMigrate(currentVersion, finalVersion)) {
unmigratedSchemaTypes.add(unmigratedSchemaType);
} }
} }
return Collections.unmodifiableSet(unmigratedSchemaTypes); return activeMigrators;
} }
/** /**
* Triggers upgrade or downgrade migration for the given schema type if its version stored in * Checks the setSchema() call won't delete any types or has incompatible types after all {@link
* AppSearch is different with the version in the request. * Migrator} has been triggered..
*
* @return {@code True} if we trigger the migration for the given type.
*/ */
public static boolean shouldTriggerMigration( public static void checkDeletedAndIncompatibleAfterMigration(
@NonNull String schemaType, @NonNull SetSchemaResponse setSchemaResponse, @NonNull Set<String> activeMigrators)
@NonNull Migrator migrator,
@NonNull Map<String, Integer> currentVersionMap,
@NonNull Map<String, Integer> finalVersionMap)
throws AppSearchException { throws AppSearchException {
Integer currentVersion = currentVersionMap.get(schemaType); Set<String> unmigratedIncompatibleTypes =
Integer finalVersion = finalVersionMap.get(schemaType); new ArraySet<>(setSchemaResponse.getIncompatibleTypes());
if (currentVersion == null) { unmigratedIncompatibleTypes.removeAll(activeMigrators);
Log.d(TAG, "The SchemaType: " + schemaType + " not present in AppSearch.");
return false; Set<String> unmigratedDeletedTypes = new ArraySet<>(setSchemaResponse.getDeletedTypes());
} unmigratedDeletedTypes.removeAll(activeMigrators);
if (finalVersion == null) {
throw new AppSearchException( // check if there are any unmigrated incompatible types or deleted types. If there
AppSearchResult.RESULT_INVALID_ARGUMENT, // are, we will getActiveMigratorsthrow an exception. That's the only case we
"Receive a migrator for schema type : " // swallowed in the AppSearchImpl#setSchema().
+ schemaType // Since the force override is false, the schema will not have been set if there are
+ ", but the schema doesn't exist in the request."); // any incompatible or deleted types.
} checkDeletedAndIncompatible(unmigratedDeletedTypes, unmigratedIncompatibleTypes);
return migrator.shouldMigrate(currentVersion, finalVersion);
} }
/** Builds a Map of SchemaType and its version of given set of {@link AppSearchSchema}. */ /** Checks the setSchema() call won't delete any types or has incompatible types. */
//TODO(b/182620003) remove this method once support migrate to another type public static void checkDeletedAndIncompatible(
@NonNull @NonNull Set<String> deletedTypes, @NonNull Set<String> incompatibleTypes)
public static Map<String, Integer> buildVersionMap( throws AppSearchException {
@NonNull Collection<AppSearchSchema> schemas, int version) { if (deletedTypes.size() > 0 || incompatibleTypes.size() > 0) {
Map<String, Integer> currentVersionMap = new ArrayMap<>(schemas.size()); String newMessage =
for (AppSearchSchema currentSchema : schemas) { "Schema is incompatible."
currentVersionMap.put(currentSchema.getSchemaType(), version); + "\n Deleted types: "
+ deletedTypes
+ "\n Incompatible types: "
+ incompatibleTypes;
throw new AppSearchException(AppSearchResult.RESULT_INVALID_SCHEMA, newMessage);
} }
return currentVersionMap;
} }
} }

View File

@@ -169,8 +169,8 @@ public class AppSearchManagerService extends SystemService {
@NonNull List<String> schemasNotDisplayedBySystem, @NonNull List<String> schemasNotDisplayedBySystem,
@NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles, @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
boolean forceOverride, boolean forceOverride,
@UserIdInt int userId,
int schemaVersion, int schemaVersion,
@UserIdInt int userId,
@NonNull IAppSearchResultCallback callback) { @NonNull IAppSearchResultCallback callback) {
Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(packageName);
Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(databaseName);