blob: fcacb5f9c11fa7fd6160eb2502711ea79d05b41a [file] [log] [blame]
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.appsearch.util;
import static android.app.appsearch.AppSearchResult.RESULT_INVALID_SCHEMA;
import android.annotation.NonNull;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.InternalSetSchemaResponse;
import android.app.appsearch.Migrator;
import android.app.appsearch.SetSchemaResponse;
import android.app.appsearch.exceptions.AppSearchException;
import android.util.ArrayMap;
import android.util.ArraySet;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
/**
* Utilities for schema migration.
*
* @hide
*/
public final class SchemaMigrationUtil {
private SchemaMigrationUtil() {}
/**
* Returns all active {@link Migrator}s that need to be triggered in this migration.
*
* <p>{@link Migrator#shouldMigrate} returns {@code true} will make the {@link Migrator} active.
*/
@NonNull
public static Map<String, Migrator> getActiveMigrators(
@NonNull Set<AppSearchSchema> existingSchemas,
@NonNull Map<String, Migrator> migrators,
int currentVersion,
int finalVersion) {
if (currentVersion == finalVersion) {
return Collections.emptyMap();
}
Set<String> existingTypes = new ArraySet<>(existingSchemas.size());
for (AppSearchSchema schema : existingSchemas) {
existingTypes.add(schema.getSchemaType());
}
Map<String, Migrator> activeMigrators = new ArrayMap<>();
for (Map.Entry<String, Migrator> entry : migrators.entrySet()) {
// The device contains the source type, and we should trigger migration for the type.
String schemaType = entry.getKey();
Migrator migrator = entry.getValue();
if (existingTypes.contains(schemaType)
&& migrator.shouldMigrate(currentVersion, finalVersion)) {
activeMigrators.put(schemaType, migrator);
}
}
return activeMigrators;
}
/**
* Checks the setSchema() call won't delete any types or has incompatible types after all {@link
* Migrator} has been triggered.
*/
public static void checkDeletedAndIncompatibleAfterMigration(
@NonNull InternalSetSchemaResponse internalSetSchemaResponse,
@NonNull Set<String> activeMigrators)
throws AppSearchException {
if (internalSetSchemaResponse.isSuccess()) {
return;
}
SetSchemaResponse setSchemaResponse = internalSetSchemaResponse.getSetSchemaResponse();
Set<String> unmigratedIncompatibleTypes =
new ArraySet<>(setSchemaResponse.getIncompatibleTypes());
unmigratedIncompatibleTypes.removeAll(activeMigrators);
Set<String> unmigratedDeletedTypes = new ArraySet<>(setSchemaResponse.getDeletedTypes());
unmigratedDeletedTypes.removeAll(activeMigrators);
// check if there are any unmigrated incompatible types or deleted types. If there
// are, we will throw an exception. That's the only case we swallowed in the
// AppSearchImpl#setSchema().
// Since the force override is false, the schema will not have been set if there are
// any incompatible or deleted types.
if (!unmigratedIncompatibleTypes.isEmpty() || !unmigratedDeletedTypes.isEmpty()) {
throw new AppSearchException(
RESULT_INVALID_SCHEMA, internalSetSchemaResponse.getErrorMessage());
}
}
}