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 package com.android.server.appsearch.external.localstorage.visibilitystore;
17 
18 import static android.app.appsearch.AppSearchResult.RESULT_NOT_FOUND;
19 
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.appsearch.AppSearchResult;
23 import android.app.appsearch.AppSearchSchema;
24 import android.app.appsearch.GenericDocument;
25 import android.app.appsearch.GetSchemaResponse;
26 import android.app.appsearch.InternalSetSchemaResponse;
27 import android.app.appsearch.InternalVisibilityConfig;
28 import android.app.appsearch.VisibilityPermissionConfig;
29 import android.app.appsearch.checker.initialization.qual.UnderInitialization;
30 import android.app.appsearch.checker.initialization.qual.UnknownInitialization;
31 import android.app.appsearch.checker.nullness.qual.RequiresNonNull;
32 import android.app.appsearch.exceptions.AppSearchException;
33 import android.app.appsearch.util.LogUtil;
34 import android.util.ArrayMap;
35 import android.util.Log;
36 
37 import com.android.server.appsearch.external.localstorage.AppSearchImpl;
38 import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
39 
40 import com.google.android.icing.proto.PersistType;
41 
42 import java.util.Arrays;
43 import java.util.Collections;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Objects;
47 import java.util.Set;
48 
49 /**
50  * Stores all visibility settings for all databases that AppSearchImpl knows about. Persists the
51  * visibility settings and reloads them on initialization.
52  *
53  * <p>The VisibilityStore creates a {@link InternalVisibilityConfig} for each schema. This config
54  * holds the visibility settings that apply to that schema. The VisibilityStore also creates a
55  * schema and documents for these {@link InternalVisibilityConfig} and has its own package and
56  * database so that its data doesn't interfere with any clients' data. It persists the document and
57  * schema through AppSearchImpl.
58  *
59  * <p>These visibility settings won't be used in AppSearch Jetpack, we only store them for clients
60  * to look up.
61  *
62  * @hide
63  */
64 public class VisibilityStore {
65     private static final String TAG = "AppSearchVisibilityStor";
66 
67     /**
68      * These cannot have any of the special characters used by AppSearchImpl (e.g. {@code
69      * AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}.
70      */
71     public static final String VISIBILITY_PACKAGE_NAME = "VS#Pkg";
72 
73     public static final String VISIBILITY_DATABASE_NAME = "VS#Db";
74     public static final String ANDROID_V_OVERLAY_DATABASE_NAME = "VS#AndroidVDb";
75 
76     /**
77      * Map of PrefixedSchemaType to InternalVisibilityConfig stores visibility information for each
78      * schema type.
79      */
80     private final Map<String, InternalVisibilityConfig> mVisibilityConfigMap = new ArrayMap<>();
81 
82     private final AppSearchImpl mAppSearchImpl;
83 
VisibilityStore(@onNull AppSearchImpl appSearchImpl)84     public VisibilityStore(@NonNull AppSearchImpl appSearchImpl) throws AppSearchException {
85         mAppSearchImpl = Objects.requireNonNull(appSearchImpl);
86 
87         GetSchemaResponse getSchemaResponse =
88                 mAppSearchImpl.getSchema(
89                         VISIBILITY_PACKAGE_NAME,
90                         VISIBILITY_DATABASE_NAME,
91                         new CallerAccess(/* callingPackageName= */ VISIBILITY_PACKAGE_NAME));
92         List<VisibilityDocumentV1> visibilityDocumentsV1s = null;
93         switch (getSchemaResponse.getVersion()) {
94             case VisibilityToDocumentConverter.SCHEMA_VERSION_DOC_PER_PACKAGE:
95                 // TODO (b/202194495) add VisibilityDocument in version 0 back instead of using
96                 //  GenericDocument.
97                 List<GenericDocument> visibilityDocumentsV0s =
98                         VisibilityStoreMigrationHelperFromV0.getVisibilityDocumentsInVersion0(
99                                 getSchemaResponse, mAppSearchImpl);
100                 visibilityDocumentsV1s =
101                         VisibilityStoreMigrationHelperFromV0.toVisibilityDocumentV1(
102                                 visibilityDocumentsV0s);
103                 // fall through
104             case VisibilityToDocumentConverter.SCHEMA_VERSION_DOC_PER_SCHEMA:
105                 if (visibilityDocumentsV1s == null) {
106                     // We need to read VisibilityDocument in Version 1 from AppSearch instead of
107                     // taking from the above step.
108                     visibilityDocumentsV1s =
109                             VisibilityStoreMigrationHelperFromV1.getVisibilityDocumentsInVersion1(
110                                     mAppSearchImpl);
111                 }
112                 setLatestSchemaAndDocuments(
113                         VisibilityStoreMigrationHelperFromV1.toVisibilityDocumentsV2(
114                                 visibilityDocumentsV1s));
115                 break;
116             case VisibilityToDocumentConverter.SCHEMA_VERSION_LATEST:
117                 verifyOrSetLatestVisibilitySchema(getSchemaResponse);
118                 // Check the version for visibility overlay database.
119                 migrateVisibilityOverlayDatabase();
120                 // Now we have the latest schema, load visibility config map.
121                 loadVisibilityConfigMap();
122                 break;
123             default:
124                 // We must did something wrong.
125                 throw new AppSearchException(
126                         AppSearchResult.RESULT_INTERNAL_ERROR,
127                         "Found unsupported visibility version: " + getSchemaResponse.getVersion());
128         }
129     }
130 
131     /**
132      * Sets visibility settings for the given {@link InternalVisibilityConfig}s. Any previous {@link
133      * InternalVisibilityConfig}s with same prefixed schema type will be overwritten.
134      *
135      * @param prefixedVisibilityConfigs List of prefixed {@link InternalVisibilityConfig}s which
136      *     contains schema type's visibility information.
137      * @throws AppSearchException on AppSearchImpl error.
138      */
setVisibility(@onNull List<InternalVisibilityConfig> prefixedVisibilityConfigs)139     public void setVisibility(@NonNull List<InternalVisibilityConfig> prefixedVisibilityConfigs)
140             throws AppSearchException {
141         Objects.requireNonNull(prefixedVisibilityConfigs);
142         // Save new setting.
143         for (int i = 0; i < prefixedVisibilityConfigs.size(); i++) {
144             // put VisibilityConfig to AppSearchImpl and mVisibilityConfigMap. If there is a
145             // VisibilityConfig with same prefixed schema exists, it will be replaced by new
146             // VisibilityConfig in both AppSearch and memory look up map.
147             InternalVisibilityConfig prefixedVisibilityConfig = prefixedVisibilityConfigs.get(i);
148             InternalVisibilityConfig oldVisibilityConfig =
149                     mVisibilityConfigMap.get(prefixedVisibilityConfig.getSchemaType());
150             mAppSearchImpl.putDocument(
151                     VISIBILITY_PACKAGE_NAME,
152                     VISIBILITY_DATABASE_NAME,
153                     VisibilityToDocumentConverter.createVisibilityDocument(
154                             prefixedVisibilityConfig),
155                     /* sendChangeNotifications= */ false,
156                     /* logger= */ null);
157 
158             // Put the android V visibility overlay document to AppSearchImpl.
159             GenericDocument androidVOverlay =
160                     VisibilityToDocumentConverter.createAndroidVOverlay(prefixedVisibilityConfig);
161             if (androidVOverlay != null) {
162                 mAppSearchImpl.putDocument(
163                         VISIBILITY_PACKAGE_NAME,
164                         ANDROID_V_OVERLAY_DATABASE_NAME,
165                         androidVOverlay,
166                         /* sendChangeNotifications= */ false,
167                         /* logger= */ null);
168             } else if (isConfigContainsAndroidVOverlay(oldVisibilityConfig)) {
169                 // We need to make sure to remove the VisibilityOverlay on disk as the current
170                 // VisibilityConfig does not have a VisibilityOverlay.
171                 // For performance improvement, we should only make the remove call if the old
172                 // VisibilityConfig contains the overlay settings.
173                 try {
174                     mAppSearchImpl.remove(
175                             VISIBILITY_PACKAGE_NAME,
176                             ANDROID_V_OVERLAY_DATABASE_NAME,
177                             VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
178                             prefixedVisibilityConfig.getSchemaType(),
179                             /* removeStatsBuilder= */ null);
180                 } catch (AppSearchException e) {
181                     // If it already doesn't exist, that is fine
182                     if (e.getResultCode() != RESULT_NOT_FOUND) {
183                         throw e;
184                     }
185                 }
186             }
187 
188             // Put the VisibilityConfig to memory look up map.
189             mVisibilityConfigMap.put(
190                     prefixedVisibilityConfig.getSchemaType(), prefixedVisibilityConfig);
191         }
192         // Now that the visibility document has been written. Persist the newly written data.
193         mAppSearchImpl.persistToDisk(PersistType.Code.LITE);
194     }
195 
196     /**
197      * Remove the visibility setting for the given prefixed schema type from both AppSearch and
198      * memory look up map.
199      */
removeVisibility(@onNull Set<String> prefixedSchemaTypes)200     public void removeVisibility(@NonNull Set<String> prefixedSchemaTypes)
201             throws AppSearchException {
202         for (String prefixedSchemaType : prefixedSchemaTypes) {
203             if (mVisibilityConfigMap.remove(prefixedSchemaType) != null) {
204                 // The deleted schema is not all-default setting, we need to remove its
205                 // VisibilityDocument from Icing.
206                 try {
207                     mAppSearchImpl.remove(
208                             VISIBILITY_PACKAGE_NAME,
209                             VISIBILITY_DATABASE_NAME,
210                             VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
211                             prefixedSchemaType,
212                             /* removeStatsBuilder= */ null);
213                 } catch (AppSearchException e) {
214                     if (e.getResultCode() == RESULT_NOT_FOUND) {
215                         // We are trying to remove this visibility setting, so it's weird but seems
216                         // to be fine if we cannot find it.
217                         Log.e(
218                                 TAG,
219                                 "Cannot find visibility document for "
220                                         + prefixedSchemaType
221                                         + " to remove.");
222                     } else {
223                         throw e;
224                     }
225                 }
226 
227                 try {
228                     mAppSearchImpl.remove(
229                             VISIBILITY_PACKAGE_NAME,
230                             ANDROID_V_OVERLAY_DATABASE_NAME,
231                             VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
232                             prefixedSchemaType,
233                             /* removeStatsBuilder= */ null);
234                 } catch (AppSearchException e) {
235                     if (e.getResultCode() == RESULT_NOT_FOUND) {
236                         // It's possible no overlay was set, so this this is fine.
237                         if (LogUtil.DEBUG) {
238                             Log.d(
239                                     TAG,
240                                     "Cannot find Android V overlay document for "
241                                             + prefixedSchemaType
242                                             + " to remove.");
243                         }
244                     } else {
245                         throw e;
246                     }
247                 }
248             }
249         }
250     }
251 
252     /** Gets the {@link InternalVisibilityConfig} for the given prefixed schema type. */
253     @Nullable
getVisibility(@onNull String prefixedSchemaType)254     public InternalVisibilityConfig getVisibility(@NonNull String prefixedSchemaType) {
255         return mVisibilityConfigMap.get(prefixedSchemaType);
256     }
257 
258     /**
259      * Loads all stored latest {@link InternalVisibilityConfig} from Icing, and put them into {@link
260      * #mVisibilityConfigMap}.
261      */
262     @RequiresNonNull("mAppSearchImpl")
loadVisibilityConfigMap(@nderInitialization VisibilityStore this)263     private void loadVisibilityConfigMap(@UnderInitialization VisibilityStore this)
264             throws AppSearchException {
265         // Populate visibility settings set
266         List<String> cachedSchemaTypes = mAppSearchImpl.getAllPrefixedSchemaTypes();
267         for (int i = 0; i < cachedSchemaTypes.size(); i++) {
268             String prefixedSchemaType = cachedSchemaTypes.get(i);
269             String packageName = PrefixUtil.getPackageName(prefixedSchemaType);
270             if (packageName.equals(VISIBILITY_PACKAGE_NAME)) {
271                 continue; // Our own package. Skip.
272             }
273 
274             GenericDocument visibilityDocument;
275             GenericDocument visibilityAndroidVOverlay = null;
276             try {
277                 // Note: We use the other clients' prefixed schema type as ids
278                 visibilityDocument =
279                         mAppSearchImpl.getDocument(
280                                 VISIBILITY_PACKAGE_NAME,
281                                 VISIBILITY_DATABASE_NAME,
282                                 VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
283                                 /* id= */ prefixedSchemaType,
284                                 /* typePropertyPaths= */ Collections.emptyMap());
285             } catch (AppSearchException e) {
286                 if (e.getResultCode() == RESULT_NOT_FOUND) {
287                     // The schema has all default setting and we won't have a VisibilityDocument for
288                     // it.
289                     continue;
290                 }
291                 // Otherwise, this is some other error we should pass up.
292                 throw e;
293             }
294 
295             try {
296                 visibilityAndroidVOverlay =
297                         mAppSearchImpl.getDocument(
298                                 VISIBILITY_PACKAGE_NAME,
299                                 ANDROID_V_OVERLAY_DATABASE_NAME,
300                                 VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
301                                 /* id= */ prefixedSchemaType,
302                                 /* typePropertyPaths= */ Collections.emptyMap());
303             } catch (AppSearchException e) {
304                 if (e.getResultCode() != RESULT_NOT_FOUND) {
305                     // This is some other error we should pass up.
306                     throw e;
307                 }
308                 // Otherwise we continue inserting into visibility document map as the overlay
309                 // map can be null
310             }
311 
312             mVisibilityConfigMap.put(
313                     prefixedSchemaType,
314                     VisibilityToDocumentConverter.createInternalVisibilityConfig(
315                             visibilityDocument, visibilityAndroidVOverlay));
316         }
317     }
318 
319     /** Set the latest version of {@link InternalVisibilityConfig} and its schema to AppSearch. */
320     @RequiresNonNull("mAppSearchImpl")
setLatestSchemaAndDocuments( @nderInitialization VisibilityStore this, @NonNull List<InternalVisibilityConfig> migratedDocuments)321     private void setLatestSchemaAndDocuments(
322             @UnderInitialization VisibilityStore this,
323             @NonNull List<InternalVisibilityConfig> migratedDocuments)
324             throws AppSearchException {
325         // The latest schema type doesn't exist yet. Add it. Set forceOverride true to
326         // delete old schema.
327         InternalSetSchemaResponse internalSetSchemaResponse =
328                 mAppSearchImpl.setSchema(
329                         VISIBILITY_PACKAGE_NAME,
330                         VISIBILITY_DATABASE_NAME,
331                         Arrays.asList(
332                                 VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA,
333                                 VisibilityPermissionConfig.SCHEMA),
334                         /* visibilityConfigs= */ Collections.emptyList(),
335                         /* forceOverride= */ true,
336                         /* version= */ VisibilityToDocumentConverter.SCHEMA_VERSION_LATEST,
337                         /* setSchemaStatsBuilder= */ null);
338         if (!internalSetSchemaResponse.isSuccess()) {
339             // Impossible case, we just set forceOverride to be true, we should never
340             // fail in incompatible changes.
341             throw new AppSearchException(
342                     AppSearchResult.RESULT_INTERNAL_ERROR,
343                     internalSetSchemaResponse.getErrorMessage());
344         }
345         InternalSetSchemaResponse internalSetAndroidVOverlaySchemaResponse =
346                 mAppSearchImpl.setSchema(
347                         VISIBILITY_PACKAGE_NAME,
348                         ANDROID_V_OVERLAY_DATABASE_NAME,
349                         Collections.singletonList(
350                                 VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA),
351                         /* visibilityConfigs= */ Collections.emptyList(),
352                         /* forceOverride= */ true,
353                         /* version= */ VisibilityToDocumentConverter
354                                 .ANDROID_V_OVERLAY_SCHEMA_VERSION_LATEST,
355                         /* setSchemaStatsBuilder= */ null);
356         if (!internalSetAndroidVOverlaySchemaResponse.isSuccess()) {
357             // Impossible case, we just set forceOverride to be true, we should never
358             // fail in incompatible changes.
359             throw new AppSearchException(
360                     AppSearchResult.RESULT_INTERNAL_ERROR,
361                     internalSetAndroidVOverlaySchemaResponse.getErrorMessage());
362         }
363         for (int i = 0; i < migratedDocuments.size(); i++) {
364             InternalVisibilityConfig migratedConfig = migratedDocuments.get(i);
365             mVisibilityConfigMap.put(migratedConfig.getSchemaType(), migratedConfig);
366             mAppSearchImpl.putDocument(
367                     VISIBILITY_PACKAGE_NAME,
368                     VISIBILITY_DATABASE_NAME,
369                     VisibilityToDocumentConverter.createVisibilityDocument(migratedConfig),
370                     /* sendChangeNotifications= */ false,
371                     /* logger= */ null);
372         }
373     }
374 
375     /**
376      * Check and migrate visibility schemas in {@link #ANDROID_V_OVERLAY_DATABASE_NAME} to {@link
377      * VisibilityToDocumentConverter#ANDROID_V_OVERLAY_SCHEMA_VERSION_LATEST}.
378      */
379     @RequiresNonNull("mAppSearchImpl")
migrateVisibilityOverlayDatabase(@nderInitialization VisibilityStore this)380     private void migrateVisibilityOverlayDatabase(@UnderInitialization VisibilityStore this)
381             throws AppSearchException {
382         GetSchemaResponse getSchemaResponse =
383                 mAppSearchImpl.getSchema(
384                         VISIBILITY_PACKAGE_NAME,
385                         ANDROID_V_OVERLAY_DATABASE_NAME,
386                         new CallerAccess(/* callingPackageName= */ VISIBILITY_PACKAGE_NAME));
387         switch (getSchemaResponse.getVersion()) {
388             case VisibilityToDocumentConverter.OVERLAY_SCHEMA_VERSION_PUBLIC_ACL_VISIBLE_TO_CONFIG:
389                 // Force override to next version. This version hasn't released to any public
390                 // version. There shouldn't have any public device in this state, so we don't
391                 // actually need to migrate any document.
392                 InternalSetSchemaResponse internalSetSchemaResponse =
393                         mAppSearchImpl.setSchema(
394                                 VISIBILITY_PACKAGE_NAME,
395                                 ANDROID_V_OVERLAY_DATABASE_NAME,
396                                 Collections.singletonList(
397                                         VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA),
398                                 /* visibilityConfigs= */ Collections.emptyList(),
399                                 /* forceOverride= */ true, // force update to nest version.
400                                 VisibilityToDocumentConverter
401                                         .ANDROID_V_OVERLAY_SCHEMA_VERSION_LATEST,
402                                 /* setSchemaStatsBuilder= */ null);
403                 if (!internalSetSchemaResponse.isSuccess()) {
404                     // Impossible case, we just set forceOverride to be true, we should never
405                     // fail in incompatible changes.
406                     throw new AppSearchException(
407                             AppSearchResult.RESULT_INTERNAL_ERROR,
408                             internalSetSchemaResponse.getErrorMessage());
409                 }
410                 break;
411             case VisibilityToDocumentConverter.OVERLAY_SCHEMA_VERSION_ALL_IN_PROTO:
412                 verifyOrSetLatestVisibilityOverlaySchema(getSchemaResponse);
413                 break;
414             default:
415                 // We must did something wrong.
416                 throw new AppSearchException(
417                         AppSearchResult.RESULT_INTERNAL_ERROR,
418                         "Found unsupported visibility version: " + getSchemaResponse.getVersion());
419         }
420     }
421 
422     /**
423      * Verify the existing visibility schema, set the latest visibilility schema if it's missing.
424      */
425     @RequiresNonNull("mAppSearchImpl")
verifyOrSetLatestVisibilitySchema( @nderInitialization VisibilityStore this, @NonNull GetSchemaResponse getSchemaResponse)426     private void verifyOrSetLatestVisibilitySchema(
427             @UnderInitialization VisibilityStore this, @NonNull GetSchemaResponse getSchemaResponse)
428             throws AppSearchException {
429         // We cannot change the schema version past 2 as detecting version "3" would hit the
430         // default block and throw an AppSearchException. This is why we added
431         // VisibilityOverlay.
432 
433         // Check Visibility schema first.
434         Set<AppSearchSchema> existingVisibilitySchema = getSchemaResponse.getSchemas();
435         // Force to override visibility schema if it contains DEPRECATED_PUBLIC_ACL_OVERLAY_SCHEMA.
436         // The DEPRECATED_PUBLIC_ACL_OVERLAY_SCHEMA was added to VISIBILITY_DATABASE_NAME and
437         // removed to ANDROID_V_OVERLAY_DATABASE_NAME. We need to force update the schema to
438         // migrate devices that have already store public acl schema.
439         // TODO(b/321326441) remove this method when we no longer to migrate devices in this state.
440         if (existingVisibilitySchema.contains(
441                         VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA)
442                 && existingVisibilitySchema.contains(VisibilityPermissionConfig.SCHEMA)
443                 && existingVisibilitySchema.contains(
444                         VisibilityToDocumentConverter.DEPRECATED_PUBLIC_ACL_OVERLAY_SCHEMA)) {
445             InternalSetSchemaResponse internalSetSchemaResponse =
446                     mAppSearchImpl.setSchema(
447                             VISIBILITY_PACKAGE_NAME,
448                             VISIBILITY_DATABASE_NAME,
449                             Arrays.asList(
450                                     VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA,
451                                     VisibilityPermissionConfig.SCHEMA),
452                             /* visibilityConfigs= */ Collections.emptyList(),
453                             /* forceOverride= */ true,
454                             /* version= */ VisibilityToDocumentConverter.SCHEMA_VERSION_LATEST,
455                             /* setSchemaStatsBuilder= */ null);
456             if (!internalSetSchemaResponse.isSuccess()) {
457                 throw new AppSearchException(
458                         AppSearchResult.RESULT_INTERNAL_ERROR,
459                         "Fail to force override deprecated visibility schema with public acl.");
460             }
461         } else if (!(existingVisibilitySchema.contains(
462                         VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA)
463                 && existingVisibilitySchema.contains(VisibilityPermissionConfig.SCHEMA))) {
464             // We must have a broken schema. Reset it to the latest version.
465             // Do NOT set forceOverride to be true here, see comment below.
466             InternalSetSchemaResponse internalSetSchemaResponse =
467                     mAppSearchImpl.setSchema(
468                             VISIBILITY_PACKAGE_NAME,
469                             VISIBILITY_DATABASE_NAME,
470                             Arrays.asList(
471                                     VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA,
472                                     VisibilityPermissionConfig.SCHEMA),
473                             /* visibilityConfigs= */ Collections.emptyList(),
474                             /* forceOverride= */ false,
475                             /* version= */ VisibilityToDocumentConverter.SCHEMA_VERSION_LATEST,
476                             /* setSchemaStatsBuilder= */ null);
477             if (!internalSetSchemaResponse.isSuccess()) {
478                 // If you hit problem here it means you made a incompatible change in
479                 // Visibility Schema without update the version number. You should bump
480                 // the version number and create a VisibilityStoreMigrationHelper which
481                 // can analyse the different between the old version and the new version
482                 // to migration user's visibility settings.
483                 throw new AppSearchException(
484                         AppSearchResult.RESULT_INTERNAL_ERROR,
485                         "Fail to set the latest visibility schema to AppSearch. "
486                                 + "You may need to update the visibility schema version "
487                                 + "number.");
488             }
489         }
490     }
491 
492     /**
493      * Verify the existing visibility overlay schema, set the latest overlay schema if it's missing.
494      */
495     @RequiresNonNull("mAppSearchImpl")
verifyOrSetLatestVisibilityOverlaySchema( @nknownInitialization VisibilityStore this, @NonNull GetSchemaResponse getAndroidVOverlaySchemaResponse)496     private void verifyOrSetLatestVisibilityOverlaySchema(
497             @UnknownInitialization VisibilityStore this,
498             @NonNull GetSchemaResponse getAndroidVOverlaySchemaResponse)
499             throws AppSearchException {
500         // Check Android V overlay schema.
501         Set<AppSearchSchema> existingAndroidVOverlaySchema =
502                 getAndroidVOverlaySchemaResponse.getSchemas();
503         if (!existingAndroidVOverlaySchema.contains(
504                 VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA)) {
505             // We must have a broken schema. Reset it to the latest version.
506             // Do NOT set forceOverride to be true here, see comment below.
507             InternalSetSchemaResponse internalSetSchemaResponse =
508                     mAppSearchImpl.setSchema(
509                             VISIBILITY_PACKAGE_NAME,
510                             ANDROID_V_OVERLAY_DATABASE_NAME,
511                             Collections.singletonList(
512                                     VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA),
513                             /* visibilityConfigs= */ Collections.emptyList(),
514                             /* forceOverride= */ false,
515                             VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA_VERSION_LATEST,
516                             /* setSchemaStatsBuilder= */ null);
517             if (!internalSetSchemaResponse.isSuccess()) {
518                 // If you hit problem here it means you made a incompatible change in
519                 // Visibility Schema. You should create new overlay schema
520                 throw new AppSearchException(
521                         AppSearchResult.RESULT_INTERNAL_ERROR,
522                         "Fail to set the overlay visibility schema to AppSearch. "
523                                 + "You may need to create new overlay schema.");
524             }
525         }
526     }
527 
528     /**
529      * Whether the given {@link InternalVisibilityConfig} contains Android V overlay settings.
530      *
531      * <p>Android V overlay {@link VisibilityToDocumentConverter#ANDROID_V_OVERLAY_SCHEMA} contains
532      * public acl and visible to config.
533      */
isConfigContainsAndroidVOverlay( @ullable InternalVisibilityConfig config)534     private static boolean isConfigContainsAndroidVOverlay(
535             @Nullable InternalVisibilityConfig config) {
536         return config != null
537                 && (config.getVisibilityConfig().getPubliclyVisibleTargetPackage() != null
538                         || !config.getVisibleToConfigs().isEmpty());
539     }
540 }
541