1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app.appsearch.util; 18 19 import static android.app.appsearch.AppSearchResult.RESULT_INVALID_SCHEMA; 20 21 import android.annotation.NonNull; 22 import android.app.appsearch.AppSearchSchema; 23 import android.app.appsearch.InternalSetSchemaResponse; 24 import android.app.appsearch.Migrator; 25 import android.app.appsearch.SetSchemaResponse; 26 import android.app.appsearch.exceptions.AppSearchException; 27 import android.util.ArrayMap; 28 import android.util.ArraySet; 29 30 import java.util.Collections; 31 import java.util.Map; 32 import java.util.Set; 33 34 /** 35 * Utilities for schema migration. 36 * 37 * @hide 38 */ 39 public final class SchemaMigrationUtil { SchemaMigrationUtil()40 private SchemaMigrationUtil() {} 41 42 /** 43 * Returns all active {@link Migrator}s that need to be triggered in this migration. 44 * 45 * <p>{@link Migrator#shouldMigrate} returns {@code true} will make the {@link Migrator} active. 46 */ 47 @NonNull getActiveMigrators( @onNull Set<AppSearchSchema> existingSchemas, @NonNull Map<String, Migrator> migrators, int currentVersion, int finalVersion)48 public static Map<String, Migrator> getActiveMigrators( 49 @NonNull Set<AppSearchSchema> existingSchemas, 50 @NonNull Map<String, Migrator> migrators, 51 int currentVersion, 52 int finalVersion) { 53 if (currentVersion == finalVersion) { 54 return Collections.emptyMap(); 55 } 56 Set<String> existingTypes = new ArraySet<>(existingSchemas.size()); 57 for (AppSearchSchema schema : existingSchemas) { 58 existingTypes.add(schema.getSchemaType()); 59 } 60 61 Map<String, Migrator> activeMigrators = new ArrayMap<>(); 62 for (Map.Entry<String, Migrator> entry : migrators.entrySet()) { 63 // The device contains the source type, and we should trigger migration for the type. 64 String schemaType = entry.getKey(); 65 Migrator migrator = entry.getValue(); 66 if (existingTypes.contains(schemaType) 67 && migrator.shouldMigrate(currentVersion, finalVersion)) { 68 activeMigrators.put(schemaType, migrator); 69 } 70 } 71 return activeMigrators; 72 } 73 74 /** 75 * Checks the setSchema() call won't delete any types or has incompatible types after all {@link 76 * Migrator} has been triggered. 77 */ checkDeletedAndIncompatibleAfterMigration( @onNull InternalSetSchemaResponse internalSetSchemaResponse, @NonNull Set<String> activeMigrators)78 public static void checkDeletedAndIncompatibleAfterMigration( 79 @NonNull InternalSetSchemaResponse internalSetSchemaResponse, 80 @NonNull Set<String> activeMigrators) 81 throws AppSearchException { 82 if (internalSetSchemaResponse.isSuccess()) { 83 return; 84 } 85 SetSchemaResponse setSchemaResponse = internalSetSchemaResponse.getSetSchemaResponse(); 86 Set<String> unmigratedIncompatibleTypes = 87 new ArraySet<>(setSchemaResponse.getIncompatibleTypes()); 88 unmigratedIncompatibleTypes.removeAll(activeMigrators); 89 90 Set<String> unmigratedDeletedTypes = new ArraySet<>(setSchemaResponse.getDeletedTypes()); 91 unmigratedDeletedTypes.removeAll(activeMigrators); 92 93 // check if there are any unmigrated incompatible types or deleted types. If there 94 // are, we will throw an exception. That's the only case we swallowed in the 95 // AppSearchImpl#setSchema(). 96 // Since the force override is false, the schema will not have been set if there are 97 // any incompatible or deleted types. 98 if (!unmigratedIncompatibleTypes.isEmpty() || !unmigratedDeletedTypes.isEmpty()) { 99 throw new AppSearchException( 100 RESULT_INVALID_SCHEMA, internalSetSchemaResponse.getErrorMessage()); 101 } 102 } 103 } 104