• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.appsearch.annotation.CanIgnoreReturnValue;
23 import android.app.appsearch.safeparcel.AbstractSafeParcelable;
24 import android.app.appsearch.safeparcel.SafeParcelable;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.util.ArraySet;
28 
29 import com.android.appsearch.flags.Flags;
30 import com.android.internal.util.Preconditions;
31 
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.Objects;
37 import java.util.Set;
38 
39 /** The response class of {@link AppSearchSession#setSchema} */
40 @SafeParcelable.Class(creator = "SetSchemaResponseCreator")
41 // TODO(b/384721898): Switch to JSpecify annotations
42 @SuppressWarnings({"HiddenSuperclass", "JSpecifyNullness"})
43 public final class SetSchemaResponse extends AbstractSafeParcelable {
44 
45     @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
46     public static final @NonNull Parcelable.Creator<SetSchemaResponse> CREATOR =
47             new SetSchemaResponseCreator();
48 
49     @Field(id = 1)
50     final List<String> mDeletedTypes;
51 
52     @Field(id = 2)
53     final List<String> mIncompatibleTypes;
54 
55     @Field(id = 3)
56     final List<String> mMigratedTypes;
57 
58     /**
59      * The migrationFailures won't be saved as a SafeParcelable field. Since:
60      *
61      * <ul>
62      *   <li>{@link MigrationFailure} is generated in {@link AppSearchSession} which will be the SDK
63      *       side in platform. We don't need to pass it from service side via binder as a part of
64      *       {@link SetSchemaResponse}.
65      *   <li>Writing multiple {@link MigrationFailure}s to SafeParcelable in {@link Builder} and
66      *       then back in constructor will be a huge waste.
67      * </ul>
68      */
69     private final List<MigrationFailure> mMigrationFailures;
70 
71     /**
72      * Cache of the inflated deleted schema types. Comes from inflating mDeletedTypes at first use
73      */
74     private @Nullable Set<String> mDeletedTypesCached;
75 
76     /**
77      * Cache of the inflated migrated schema types. Comes from inflating mMigratedTypes at first
78      * use.
79      */
80     private @Nullable Set<String> mMigratedTypesCached;
81 
82     /**
83      * Cache of the inflated incompatible schema types. Comes from inflating mIncompatibleTypes at
84      * first use.
85      */
86     private @Nullable Set<String> mIncompatibleTypesCached;
87 
88     @Constructor
SetSchemaResponse( @aramid = 1) @onNull List<String> deletedTypes, @Param(id = 2) @NonNull List<String> incompatibleTypes, @Param(id = 3) @NonNull List<String> migratedTypes)89     SetSchemaResponse(
90             @Param(id = 1) @NonNull List<String> deletedTypes,
91             @Param(id = 2) @NonNull List<String> incompatibleTypes,
92             @Param(id = 3) @NonNull List<String> migratedTypes) {
93         mDeletedTypes = deletedTypes;
94         mIncompatibleTypes = incompatibleTypes;
95         mMigratedTypes = migratedTypes;
96         mMigrationFailures = Collections.emptyList();
97     }
98 
SetSchemaResponse( @onNull List<String> deletedTypes, @NonNull List<String> incompatibleTypes, @NonNull List<String> migratedTypes, @NonNull List<MigrationFailure> migrationFailures)99     SetSchemaResponse(
100             @NonNull List<String> deletedTypes,
101             @NonNull List<String> incompatibleTypes,
102             @NonNull List<String> migratedTypes,
103             @NonNull List<MigrationFailure> migrationFailures) {
104         mDeletedTypes = deletedTypes;
105         mIncompatibleTypes = incompatibleTypes;
106         mMigratedTypes = migratedTypes;
107         mMigrationFailures = Objects.requireNonNull(migrationFailures);
108     }
109 
110     /**
111      * Returns a {@link List} of all failed {@link MigrationFailure}.
112      *
113      * <p>A {@link MigrationFailure} will be generated if the system trying to save a post-migrated
114      * {@link GenericDocument} but fail.
115      *
116      * <p>{@link MigrationFailure} contains the namespace, id and schemaType of the post-migrated
117      * {@link GenericDocument} and the error reason. Mostly it will be mismatch the schema it
118      * migrated to.
119      */
getMigrationFailures()120     public @NonNull List<MigrationFailure> getMigrationFailures() {
121         return Collections.unmodifiableList(mMigrationFailures);
122     }
123 
124     /**
125      * Returns a {@link Set} of deleted schema types.
126      *
127      * <p>A "deleted" type is a schema type that was previously a part of the database schema but
128      * was not present in the {@link SetSchemaRequest} object provided in the {@link
129      * AppSearchSession#setSchema} call.
130      *
131      * <p>Documents for a deleted type are removed from the database.
132      */
getDeletedTypes()133     public @NonNull Set<String> getDeletedTypes() {
134         if (mDeletedTypesCached == null) {
135             mDeletedTypesCached = new ArraySet<>(Objects.requireNonNull(mDeletedTypes));
136         }
137         return Collections.unmodifiableSet(mDeletedTypesCached);
138     }
139 
140     /**
141      * Returns a {@link Set} of schema type that were migrated by the {@link
142      * AppSearchSession#setSchema} call.
143      *
144      * <p>A "migrated" type is a schema type that has triggered a {@link Migrator} instance to
145      * migrate documents of the schema type to another schema type, or to another version of the
146      * schema type.
147      *
148      * <p>If a document fails to be migrated, a {@link MigrationFailure} will be generated for that
149      * document.
150      *
151      * @see Migrator
152      */
getMigratedTypes()153     public @NonNull Set<String> getMigratedTypes() {
154         if (mMigratedTypesCached == null) {
155             mMigratedTypesCached = new ArraySet<>(Objects.requireNonNull(mMigratedTypes));
156         }
157         return Collections.unmodifiableSet(mMigratedTypesCached);
158     }
159 
160     /**
161      * Returns a {@link Set} of schema type whose new definitions set in the {@link
162      * AppSearchSession#setSchema} call were incompatible with the pre-existing schema.
163      *
164      * <p>If a {@link Migrator} is provided for this type and the migration is success triggered.
165      * The type will also appear in {@link #getMigratedTypes()}.
166      *
167      * @see SetSchemaRequest
168      * @see AppSearchSession#setSchema
169      * @see SetSchemaRequest.Builder#setForceOverride
170      */
getIncompatibleTypes()171     public @NonNull Set<String> getIncompatibleTypes() {
172         if (mIncompatibleTypesCached == null) {
173             mIncompatibleTypesCached = new ArraySet<>(Objects.requireNonNull(mIncompatibleTypes));
174         }
175         return Collections.unmodifiableSet(mIncompatibleTypesCached);
176     }
177 
178     /** Builder for {@link SetSchemaResponse} objects. */
179     public static final class Builder {
180         private List<MigrationFailure> mMigrationFailures = new ArrayList<>();
181         private ArrayList<String> mDeletedTypes = new ArrayList<>();
182         private ArrayList<String> mMigratedTypes = new ArrayList<>();
183         private ArrayList<String> mIncompatibleTypes = new ArrayList<>();
184         private boolean mBuilt = false;
185 
186         /**
187          * Creates a new {@link SetSchemaResponse.Builder} from the given SetSchemaResponse.
188          *
189          * @hide
190          */
Builder(@onNull SetSchemaResponse setSchemaResponse)191         public Builder(@NonNull SetSchemaResponse setSchemaResponse) {
192             Objects.requireNonNull(setSchemaResponse);
193             mDeletedTypes.addAll(setSchemaResponse.getDeletedTypes());
194             mIncompatibleTypes.addAll(setSchemaResponse.getIncompatibleTypes());
195             mMigratedTypes.addAll(setSchemaResponse.getMigratedTypes());
196             mMigrationFailures.addAll(setSchemaResponse.getMigrationFailures());
197         }
198 
199         /** Create a {@link Builder} object} */
Builder()200         public Builder() {}
201 
202         /** Adds {@link MigrationFailure}s to the list of migration failures. */
203         @CanIgnoreReturnValue
addMigrationFailures( @onNull Collection<MigrationFailure> migrationFailures)204         public @NonNull Builder addMigrationFailures(
205                 @NonNull Collection<MigrationFailure> migrationFailures) {
206             Objects.requireNonNull(migrationFailures);
207             resetIfBuilt();
208             mMigrationFailures.addAll(migrationFailures);
209             return this;
210         }
211 
212         /** Adds a {@link MigrationFailure} to the list of migration failures. */
213         @CanIgnoreReturnValue
addMigrationFailure(@onNull MigrationFailure migrationFailure)214         public @NonNull Builder addMigrationFailure(@NonNull MigrationFailure migrationFailure) {
215             Objects.requireNonNull(migrationFailure);
216             resetIfBuilt();
217             mMigrationFailures.add(migrationFailure);
218             return this;
219         }
220 
221         /** Adds {@code deletedTypes} to the list of deleted schema types. */
222         @CanIgnoreReturnValue
addDeletedTypes(@onNull Collection<String> deletedTypes)223         public @NonNull Builder addDeletedTypes(@NonNull Collection<String> deletedTypes) {
224             Objects.requireNonNull(deletedTypes);
225             resetIfBuilt();
226             mDeletedTypes.addAll(deletedTypes);
227             return this;
228         }
229 
230         /** Adds one {@code deletedType} to the list of deleted schema types. */
231         @CanIgnoreReturnValue
addDeletedType(@onNull String deletedType)232         public @NonNull Builder addDeletedType(@NonNull String deletedType) {
233             Objects.requireNonNull(deletedType);
234             resetIfBuilt();
235             mDeletedTypes.add(deletedType);
236             return this;
237         }
238 
239         /** Adds {@code incompatibleTypes} to the list of incompatible schema types. */
240         @CanIgnoreReturnValue
addIncompatibleTypes( @onNull Collection<String> incompatibleTypes)241         public @NonNull Builder addIncompatibleTypes(
242                 @NonNull Collection<String> incompatibleTypes) {
243             Objects.requireNonNull(incompatibleTypes);
244             resetIfBuilt();
245             mIncompatibleTypes.addAll(incompatibleTypes);
246             return this;
247         }
248 
249         /** Adds one {@code incompatibleType} to the list of incompatible schema types. */
250         @CanIgnoreReturnValue
addIncompatibleType(@onNull String incompatibleType)251         public @NonNull Builder addIncompatibleType(@NonNull String incompatibleType) {
252             Objects.requireNonNull(incompatibleType);
253             resetIfBuilt();
254             mIncompatibleTypes.add(incompatibleType);
255             return this;
256         }
257 
258         /** Adds {@code migratedTypes} to the list of migrated schema types. */
259         @CanIgnoreReturnValue
addMigratedTypes(@onNull Collection<String> migratedTypes)260         public @NonNull Builder addMigratedTypes(@NonNull Collection<String> migratedTypes) {
261             Objects.requireNonNull(migratedTypes);
262             resetIfBuilt();
263             mMigratedTypes.addAll(migratedTypes);
264             return this;
265         }
266 
267         /** Adds one {@code migratedType} to the list of migrated schema types. */
268         @CanIgnoreReturnValue
addMigratedType(@onNull String migratedType)269         public @NonNull Builder addMigratedType(@NonNull String migratedType) {
270             Objects.requireNonNull(migratedType);
271             resetIfBuilt();
272             mMigratedTypes.add(migratedType);
273             return this;
274         }
275 
276         /** Builds a {@link SetSchemaResponse} object. */
build()277         public @NonNull SetSchemaResponse build() {
278             mBuilt = true;
279             // Avoid converting the potential thousands of MigrationFailures to Pracelable and
280             // back just for put in bundle. In platform, we should set MigrationFailures in
281             // AppSearchSession after we pass SetSchemaResponse via binder.
282             return new SetSchemaResponse(
283                     mDeletedTypes, mIncompatibleTypes, mMigratedTypes, mMigrationFailures);
284         }
285 
resetIfBuilt()286         private void resetIfBuilt() {
287             if (mBuilt) {
288                 mMigrationFailures = new ArrayList<>(mMigrationFailures);
289                 mDeletedTypes = new ArrayList<>(mDeletedTypes);
290                 mMigratedTypes = new ArrayList<>(mMigratedTypes);
291                 mIncompatibleTypes = new ArrayList<>(mIncompatibleTypes);
292                 mBuilt = false;
293             }
294         }
295     }
296 
297     @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
298     @Override
writeToParcel(@onNull Parcel dest, int flags)299     public void writeToParcel(@NonNull Parcel dest, int flags) {
300         SetSchemaResponseCreator.writeToParcel(this, dest, flags);
301     }
302 
303     /**
304      * The class represents a post-migrated {@link GenericDocument} that failed to be saved by
305      * {@link AppSearchSession#setSchema}.
306      */
307     @SafeParcelable.Class(creator = "MigrationFailureCreator")
308     @SuppressWarnings("HiddenSuperclass")
309     public static class MigrationFailure extends AbstractSafeParcelable {
310 
311         @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
312         public static final @NonNull Parcelable.Creator<MigrationFailure> CREATOR =
313                 new MigrationFailureCreator();
314 
315         @Field(id = 1, getter = "getNamespace")
316         private final String mNamespace;
317 
318         @Field(id = 2, getter = "getDocumentId")
319         private final String mDocumentId;
320 
321         @Field(id = 3, getter = "getSchemaType")
322         private final String mSchemaType;
323 
324         @Field(id = 4)
325         final @Nullable String mErrorMessage;
326 
327         @Field(id = 5)
328         final int mResultCode;
329 
330         @Constructor
MigrationFailure( @aramid = 1) @onNull String namespace, @Param(id = 2) @NonNull String documentId, @Param(id = 3) @NonNull String schemaType, @Param(id = 4) @Nullable String errorMessage, @Param(id = 5) int resultCode)331         MigrationFailure(
332                 @Param(id = 1) @NonNull String namespace,
333                 @Param(id = 2) @NonNull String documentId,
334                 @Param(id = 3) @NonNull String schemaType,
335                 @Param(id = 4) @Nullable String errorMessage,
336                 @Param(id = 5) int resultCode) {
337             mNamespace = namespace;
338             mDocumentId = documentId;
339             mSchemaType = schemaType;
340             mErrorMessage = errorMessage;
341             mResultCode = resultCode;
342         }
343 
344         /**
345          * Constructs a new {@link MigrationFailure}.
346          *
347          * @param namespace The namespace of the document which failed to be migrated.
348          * @param documentId The id of the document which failed to be migrated.
349          * @param schemaType The type of the document which failed to be migrated.
350          * @param failedResult The reason why the document failed to be indexed.
351          * @throws IllegalArgumentException if the provided {@code failedResult} was not a failure.
352          */
MigrationFailure( @onNull String namespace, @NonNull String documentId, @NonNull String schemaType, @NonNull AppSearchResult<?> failedResult)353         public MigrationFailure(
354                 @NonNull String namespace,
355                 @NonNull String documentId,
356                 @NonNull String schemaType,
357                 @NonNull AppSearchResult<?> failedResult) {
358             mNamespace = namespace;
359             mDocumentId = documentId;
360             mSchemaType = schemaType;
361 
362             Objects.requireNonNull(failedResult);
363             Preconditions.checkArgument(
364                     !failedResult.isSuccess(), "failedResult was actually successful");
365             mErrorMessage = failedResult.getErrorMessage();
366             mResultCode = failedResult.getResultCode();
367         }
368 
369         /** Returns the namespace of the {@link GenericDocument} that failed to be migrated. */
getNamespace()370         public @NonNull String getNamespace() {
371             return mNamespace;
372         }
373 
374         /** Returns the id of the {@link GenericDocument} that failed to be migrated. */
getDocumentId()375         public @NonNull String getDocumentId() {
376             return mDocumentId;
377         }
378 
379         /** Returns the schema type of the {@link GenericDocument} that failed to be migrated. */
getSchemaType()380         public @NonNull String getSchemaType() {
381             return mSchemaType;
382         }
383 
384         /**
385          * Returns the {@link AppSearchResult} that indicates why the post-migration {@link
386          * GenericDocument} failed to be indexed.
387          */
getAppSearchResult()388         public @NonNull AppSearchResult<Void> getAppSearchResult() {
389             return AppSearchResult.newFailedResult(mResultCode, mErrorMessage);
390         }
391 
392         @Override
toString()393         public @NonNull String toString() {
394             return "MigrationFailure { schemaType: "
395                     + getSchemaType()
396                     + ", namespace: "
397                     + getNamespace()
398                     + ", documentId: "
399                     + getDocumentId()
400                     + ", appSearchResult: "
401                     + getAppSearchResult().toString()
402                     + "}";
403         }
404 
405         @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
406         @Override
writeToParcel(@onNull Parcel dest, int flags)407         public void writeToParcel(@NonNull Parcel dest, int flags) {
408             MigrationFailureCreator.writeToParcel(this, dest, flags);
409         }
410     }
411 }
412