Merge "Implement schema migration to another type in framework." into sc-dev
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}<{@link Bundle}>, where the value are
|
* {@link AppSearchResult}<{@link Bundle}>, 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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user