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.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SuppressLint;
24 import android.app.appsearch.annotation.CanIgnoreReturnValue;
25 import android.app.appsearch.safeparcel.AbstractSafeParcelable;
26 import android.app.appsearch.safeparcel.SafeParcelable;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.util.ArrayMap;
30 import android.util.ArraySet;
31 
32 import com.android.appsearch.flags.Flags;
33 
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Objects;
39 import java.util.Set;
40 
41 /** The response class of {@link AppSearchSession#getSchema} */
42 @SafeParcelable.Class(creator = "GetSchemaResponseCreator")
43 @SuppressWarnings("HiddenSuperclass")
44 public final class GetSchemaResponse extends AbstractSafeParcelable {
45 
46     @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
47     @NonNull
48     public static final Parcelable.Creator<GetSchemaResponse> CREATOR =
49             new GetSchemaResponseCreator();
50 
51     @Field(id = 1, getter = "getVersion")
52     private final int mVersion;
53 
54     @Field(id = 2)
55     final List<AppSearchSchema> mSchemas;
56 
57     /**
58      * List of VisibilityConfigs for the current schema. May be {@code null} if retrieving the
59      * visibility settings is not possible on the current backend.
60      */
61     @Field(id = 3)
62     @Nullable
63     final List<InternalVisibilityConfig> mVisibilityConfigs;
64 
65     /**
66      * This set contains all schemas most recently successfully provided to {@link
67      * AppSearchSession#setSchema}. We do lazy fetch, the object will be created when you first time
68      * fetch it.
69      */
70     @Nullable private Set<AppSearchSchema> mSchemasCached;
71 
72     /**
73      * This Set contains all schemas that are not displayed by the system. All values in the set are
74      * prefixed with the package-database prefix. We do lazy fetch, the object will be created when
75      * you first time fetch it.
76      */
77     @Nullable private Set<String> mSchemasNotDisplayedBySystemCached;
78 
79     /**
80      * This map contains all schemas and {@link PackageIdentifier} that has access to the schema.
81      * All keys in the map are prefixed with the package-database prefix. We do lazy fetch, the
82      * object will be created when you first time fetch it.
83      */
84     @Nullable private Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackagesCached;
85 
86     /**
87      * This map contains all schemas and Android Permissions combinations that are required to
88      * access the schema. All keys in the map are prefixed with the package-database prefix. We do
89      * lazy fetch, the object will be created when you first time fetch it. The Map is constructed
90      * in ANY-ALL cases. The querier could read the {@link GenericDocument} objects under the {@code
91      * schemaType} if they hold ALL required permissions of ANY combinations.
92      *
93      * @see SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility(String, Set)
94      */
95     @Nullable private Map<String, Set<Set<Integer>>> mSchemasVisibleToPermissionsCached;
96 
97     /**
98      * This map contains all publicly visible schemas and the {@link PackageIdentifier} specifying
99      * the package that the schemas are from.
100      */
101     @Nullable private Map<String, PackageIdentifier> mPubliclyVisibleSchemasCached;
102 
103     /**
104      * This map contains all {@link SchemaVisibilityConfig}s that has access to the schema. All keys
105      * in the map are prefixed with the package-database prefix. We do lazy fetch, the object will
106      * be created when you first time fetch it.
107      */
108     @Nullable private Map<String, Set<SchemaVisibilityConfig>> mSchemasVisibleToConfigsCached;
109 
110     @Constructor
GetSchemaResponse( @aramid = 1) int version, @Param(id = 2) @NonNull List<AppSearchSchema> schemas, @Param(id = 3) @Nullable List<InternalVisibilityConfig> visibilityConfigs)111     GetSchemaResponse(
112             @Param(id = 1) int version,
113             @Param(id = 2) @NonNull List<AppSearchSchema> schemas,
114             @Param(id = 3) @Nullable List<InternalVisibilityConfig> visibilityConfigs) {
115         mVersion = version;
116         mSchemas = Objects.requireNonNull(schemas);
117         mVisibilityConfigs = visibilityConfigs;
118     }
119 
120     /**
121      * Returns the overall database schema version.
122      *
123      * <p>If the database is empty, 0 will be returned.
124      */
125     @IntRange(from = 0)
getVersion()126     public int getVersion() {
127         return mVersion;
128     }
129 
130     /**
131      * Return the schemas most recently successfully provided to {@link AppSearchSession#setSchema}.
132      */
133     @NonNull
getSchemas()134     public Set<AppSearchSchema> getSchemas() {
135         if (mSchemasCached == null) {
136             mSchemasCached = Collections.unmodifiableSet(new ArraySet<>(mSchemas));
137         }
138         return mSchemasCached;
139     }
140 
141     /**
142      * Returns all the schema types that are opted out of being displayed and visible on any system
143      * UI surface.
144      */
145     @NonNull
getSchemaTypesNotDisplayedBySystem()146     public Set<String> getSchemaTypesNotDisplayedBySystem() {
147         List<InternalVisibilityConfig> visibilityConfigs = getVisibilityConfigsOrThrow();
148         if (mSchemasNotDisplayedBySystemCached == null) {
149             Set<String> copy = new ArraySet<>();
150             for (int i = 0; i < visibilityConfigs.size(); i++) {
151                 if (visibilityConfigs.get(i).isNotDisplayedBySystem()) {
152                     copy.add(visibilityConfigs.get(i).getSchemaType());
153                 }
154             }
155             mSchemasNotDisplayedBySystemCached = Collections.unmodifiableSet(copy);
156         }
157         return mSchemasNotDisplayedBySystemCached;
158     }
159 
160     /**
161      * Returns a mapping of schema types to the set of packages that have access to that schema
162      * type.
163      */
164     @NonNull
getSchemaTypesVisibleToPackages()165     public Map<String, Set<PackageIdentifier>> getSchemaTypesVisibleToPackages() {
166         List<InternalVisibilityConfig> visibilityConfigs = getVisibilityConfigsOrThrow();
167         if (mSchemasVisibleToPackagesCached == null) {
168             Map<String, Set<PackageIdentifier>> copy = new ArrayMap<>();
169             for (int i = 0; i < visibilityConfigs.size(); i++) {
170                 InternalVisibilityConfig visibilityConfig = visibilityConfigs.get(i);
171                 List<PackageIdentifier> visibleToPackages =
172                         visibilityConfig.getVisibilityConfig().getAllowedPackages();
173                 if (!visibleToPackages.isEmpty()) {
174                     copy.put(
175                             visibilityConfig.getSchemaType(),
176                             Collections.unmodifiableSet(new ArraySet<>(visibleToPackages)));
177                 }
178             }
179             mSchemasVisibleToPackagesCached = Collections.unmodifiableMap(copy);
180         }
181         return mSchemasVisibleToPackagesCached;
182     }
183 
184     /**
185      * Returns a mapping of schema types to the set of {@link android.Manifest.permission}
186      * combination sets that querier must hold to access that schema type.
187      *
188      * <p>The querier could read the {@link GenericDocument} objects under the {@code schemaType} if
189      * they holds ALL required permissions of ANY of the individual value sets.
190      *
191      * <p>For example, if the Map contains {@code {% verbatim %}{{permissionA, PermissionB}, {
192      * PermissionC, PermissionD}, {PermissionE}}{% endverbatim %}}.
193      *
194      * <ul>
195      *   <li>A querier holding both PermissionA and PermissionB has access.
196      *   <li>A querier holding both PermissionC and PermissionD has access.
197      *   <li>A querier holding only PermissionE has access.
198      *   <li>A querier holding both PermissionA and PermissionE has access.
199      *   <li>A querier holding only PermissionA doesn't have access.
200      *   <li>A querier holding only PermissionA and PermissionC doesn't have access.
201      * </ul>
202      *
203      * @return The map contains schema type and all combinations of required permission for querier
204      *     to access it. The supported Permission are {@link SetSchemaRequest#READ_SMS}, {@link
205      *     SetSchemaRequest#READ_CALENDAR}, {@link SetSchemaRequest#READ_CONTACTS}, {@link
206      *     SetSchemaRequest#READ_EXTERNAL_STORAGE}, {@link
207      *     SetSchemaRequest#READ_HOME_APP_SEARCH_DATA} and {@link
208      *     SetSchemaRequest#READ_ASSISTANT_APP_SEARCH_DATA}.
209      */
210     // TODO(b/237388235): add enterprise permissions to javadocs after they're unhidden
211 
212     @NonNull
getRequiredPermissionsForSchemaTypeVisibility()213     public Map<String, Set<Set<Integer>>> getRequiredPermissionsForSchemaTypeVisibility() {
214         List<InternalVisibilityConfig> visibilityConfigs = getVisibilityConfigsOrThrow();
215         if (mSchemasVisibleToPermissionsCached == null) {
216             Map<String, Set<Set<Integer>>> copy = new ArrayMap<>();
217             for (int i = 0; i < visibilityConfigs.size(); i++) {
218                 InternalVisibilityConfig visibilityConfig = visibilityConfigs.get(i);
219                 Set<Set<Integer>> visibleToPermissions =
220                         visibilityConfig.getVisibilityConfig().getRequiredPermissions();
221                 if (!visibleToPermissions.isEmpty()) {
222                     copy.put(
223                             visibilityConfig.getSchemaType(),
224                             Collections.unmodifiableSet(visibleToPermissions));
225                 }
226             }
227             mSchemasVisibleToPermissionsCached = Collections.unmodifiableMap(copy);
228         }
229         return mSchemasVisibleToPermissionsCached;
230     }
231 
232     /**
233      * Returns a mapping of publicly visible schemas to the {@link PackageIdentifier} specifying the
234      * package the schemas are from.
235      *
236      * <p>If no schemas have been set as publicly visible, an empty set will be returned.
237      */
238     @FlaggedApi(Flags.FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA)
239     @NonNull
getPubliclyVisibleSchemas()240     public Map<String, PackageIdentifier> getPubliclyVisibleSchemas() {
241         List<InternalVisibilityConfig> visibilityConfigs = getVisibilityConfigsOrThrow();
242         if (mPubliclyVisibleSchemasCached == null) {
243             Map<String, PackageIdentifier> copy = new ArrayMap<>();
244             for (int i = 0; i < visibilityConfigs.size(); i++) {
245                 InternalVisibilityConfig visibilityConfig = visibilityConfigs.get(i);
246                 PackageIdentifier publiclyVisibleTargetPackage =
247                         visibilityConfig.getVisibilityConfig().getPubliclyVisibleTargetPackage();
248                 if (publiclyVisibleTargetPackage != null) {
249                     copy.put(visibilityConfig.getSchemaType(), publiclyVisibleTargetPackage);
250                 }
251             }
252             mPubliclyVisibleSchemasCached = Collections.unmodifiableMap(copy);
253         }
254         return mPubliclyVisibleSchemasCached;
255     }
256 
257     /**
258      * Returns a mapping of schema types to the set of {@link SchemaVisibilityConfig} that have
259      * access to that schema type.
260      *
261      * @see SetSchemaRequest.Builder#addSchemaTypeVisibleToConfig
262      */
263     @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
264     @NonNull
getSchemaTypesVisibleToConfigs()265     public Map<String, Set<SchemaVisibilityConfig>> getSchemaTypesVisibleToConfigs() {
266         List<InternalVisibilityConfig> visibilityConfigs = getVisibilityConfigsOrThrow();
267         if (mSchemasVisibleToConfigsCached == null) {
268             Map<String, Set<SchemaVisibilityConfig>> copy = new ArrayMap<>();
269             for (int i = 0; i < visibilityConfigs.size(); i++) {
270                 InternalVisibilityConfig visibilityConfig = visibilityConfigs.get(i);
271                 Set<SchemaVisibilityConfig> nestedVisibilityConfigs =
272                         visibilityConfig.getVisibleToConfigs();
273                 if (!nestedVisibilityConfigs.isEmpty()) {
274                     copy.put(
275                             visibilityConfig.getSchemaType(),
276                             Collections.unmodifiableSet(nestedVisibilityConfigs));
277                 }
278             }
279             mSchemasVisibleToConfigsCached = Collections.unmodifiableMap(copy);
280         }
281         return mSchemasVisibleToConfigsCached;
282     }
283 
284     @NonNull
getVisibilityConfigsOrThrow()285     private List<InternalVisibilityConfig> getVisibilityConfigsOrThrow() {
286         List<InternalVisibilityConfig> visibilityConfigs = mVisibilityConfigs;
287         if (visibilityConfigs == null) {
288             throw new UnsupportedOperationException(
289                     "Get visibility setting is not supported with "
290                             + "this backend/Android API level combination.");
291         }
292         return visibilityConfigs;
293     }
294 
295     @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
296     @Override
writeToParcel(@onNull Parcel dest, int flags)297     public void writeToParcel(@NonNull Parcel dest, int flags) {
298         GetSchemaResponseCreator.writeToParcel(this, dest, flags);
299     }
300 
301     /** Builder for {@link GetSchemaResponse} objects. */
302     public static final class Builder {
303         private int mVersion = 0;
304         private ArrayList<AppSearchSchema> mSchemas = new ArrayList<>();
305 
306         /**
307          * Creates the object when we actually set them. If we never set visibility settings, we
308          * should throw {@link UnsupportedOperationException} in the visibility getters.
309          */
310         @Nullable private Map<String, InternalVisibilityConfig.Builder> mVisibilityConfigBuilders;
311 
312         private boolean mBuilt = false;
313 
314         /** Create a {@link Builder} object} */
Builder()315         public Builder() {
316             setVisibilitySettingSupported(true);
317         }
318 
319         /**
320          * Sets the database overall schema version.
321          *
322          * <p>Default version is 0
323          */
324         @CanIgnoreReturnValue
325         @NonNull
setVersion(@ntRangefrom = 0) int version)326         public Builder setVersion(@IntRange(from = 0) int version) {
327             resetIfBuilt();
328             mVersion = version;
329             return this;
330         }
331 
332         /** Adds one {@link AppSearchSchema} to the schema list. */
333         @CanIgnoreReturnValue
334         @NonNull
addSchema(@onNull AppSearchSchema schema)335         public Builder addSchema(@NonNull AppSearchSchema schema) {
336             Objects.requireNonNull(schema);
337             resetIfBuilt();
338             mSchemas.add(schema);
339             return this;
340         }
341 
342         /**
343          * Sets whether or not documents from the provided {@code schemaType} will be displayed and
344          * visible on any system UI surface.
345          *
346          * @param schemaType The name of an {@link AppSearchSchema} within the same {@link
347          *     GetSchemaResponse}, which won't be displayed by system.
348          */
349         // Getter getSchemaTypesNotDisplayedBySystem returns plural objects.
350         @CanIgnoreReturnValue
351         @SuppressLint("MissingGetterMatchingBuilder")
352         @NonNull
addSchemaTypeNotDisplayedBySystem(@onNull String schemaType)353         public Builder addSchemaTypeNotDisplayedBySystem(@NonNull String schemaType) {
354             Objects.requireNonNull(schemaType);
355             resetIfBuilt();
356             InternalVisibilityConfig.Builder visibilityConfigBuilder =
357                     getOrCreateVisibilityConfigBuilder(schemaType);
358             visibilityConfigBuilder.setNotDisplayedBySystem(true);
359             return this;
360         }
361 
362         /**
363          * Sets whether or not documents from the provided {@code schemaType} can be read by the
364          * specified package.
365          *
366          * <p>Each package is represented by a {@link PackageIdentifier}, containing a package name
367          * and a byte array of type {@link android.content.pm.PackageManager#CERT_INPUT_SHA256}.
368          *
369          * <p>To opt into one-way data sharing with another application, the developer will need to
370          * explicitly grant the other application’s package name and certificate Read access to its
371          * data.
372          *
373          * <p>For two-way data sharing, both applications need to explicitly grant Read access to
374          * one another.
375          *
376          * @param schemaType The schema type to set visibility on.
377          * @param packageIdentifiers Represents the package that has access to the given schema
378          *     type.
379          */
380         // Getter getSchemaTypesVisibleToPackages returns a map contains all schema types.
381         @CanIgnoreReturnValue
382         @SuppressLint("MissingGetterMatchingBuilder")
383         @NonNull
setSchemaTypeVisibleToPackages( @onNull String schemaType, @NonNull Set<PackageIdentifier> packageIdentifiers)384         public Builder setSchemaTypeVisibleToPackages(
385                 @NonNull String schemaType, @NonNull Set<PackageIdentifier> packageIdentifiers) {
386             Objects.requireNonNull(schemaType);
387             Objects.requireNonNull(packageIdentifiers);
388             resetIfBuilt();
389             InternalVisibilityConfig.Builder visibilityConfigBuilder =
390                     getOrCreateVisibilityConfigBuilder(schemaType);
391             for (PackageIdentifier packageIdentifier : packageIdentifiers) {
392                 visibilityConfigBuilder.addVisibleToPackage(packageIdentifier);
393             }
394             return this;
395         }
396 
397         /**
398          * Sets a set of required {@link android.Manifest.permission} combinations to the given
399          * schema type.
400          *
401          * <p>The querier could read the {@link GenericDocument} objects under the {@code
402          * schemaType} if they holds ALL required permissions of ANY of the individual value sets.
403          *
404          * <p>For example, if the Map contains {@code {% verbatim %}{{permissionA, PermissionB},
405          * {PermissionC, PermissionD}, {PermissionE}}{% endverbatim %}}.
406          *
407          * <ul>
408          *   <li>A querier holds both PermissionA and PermissionB has access.
409          *   <li>A querier holds both PermissionC and PermissionD has access.
410          *   <li>A querier holds only PermissionE has access.
411          *   <li>A querier holds both PermissionA and PermissionE has access.
412          *   <li>A querier holds only PermissionA doesn't have access.
413          *   <li>A querier holds both PermissionA and PermissionC doesn't have access.
414          * </ul>
415          *
416          * @param schemaType The schema type to set visibility on.
417          * @param visibleToPermissionSets The Sets of Android permissions that will be required to
418          *     access the given schema.
419          * @see android.Manifest.permission#READ_SMS
420          * @see android.Manifest.permission#READ_CALENDAR
421          * @see android.Manifest.permission#READ_CONTACTS
422          * @see android.Manifest.permission#READ_EXTERNAL_STORAGE
423          * @see android.Manifest.permission#READ_HOME_APP_SEARCH_DATA
424          * @see android.Manifest.permission#READ_ASSISTANT_APP_SEARCH_DATA
425          */
426         // TODO(b/237388235): add enterprise permissions to javadocs after they're unhidden
427         // Getter getRequiredPermissionsForSchemaTypeVisibility returns a map for all schemaTypes.
428         @CanIgnoreReturnValue
429         @SuppressLint("MissingGetterMatchingBuilder")
430         // @SetSchemaRequest is an IntDef annotation applied to Set<Set<Integer>>.
431         @SuppressWarnings("SupportAnnotationUsage")
432         @NonNull
setRequiredPermissionsForSchemaTypeVisibility( @onNull String schemaType, @SetSchemaRequest.AppSearchSupportedPermission @NonNull Set<Set<Integer>> visibleToPermissionSets)433         public Builder setRequiredPermissionsForSchemaTypeVisibility(
434                 @NonNull String schemaType,
435                 @SetSchemaRequest.AppSearchSupportedPermission @NonNull
436                         Set<Set<Integer>> visibleToPermissionSets) {
437             Objects.requireNonNull(schemaType);
438             Objects.requireNonNull(visibleToPermissionSets);
439             resetIfBuilt();
440             InternalVisibilityConfig.Builder visibilityConfigBuilder =
441                     getOrCreateVisibilityConfigBuilder(schemaType);
442             for (Set<Integer> visibleToPermissions : visibleToPermissionSets) {
443                 visibilityConfigBuilder.addVisibleToPermissions(visibleToPermissions);
444             }
445             return this;
446         }
447 
448         /**
449          * Specify that the schema should be publicly available, to packages which already have
450          * visibility to {@code packageIdentifier}.
451          *
452          * @param schemaType the schema to make publicly accessible.
453          * @param packageIdentifier the package from which the document schema is from.
454          * @see SetSchemaRequest.Builder#setPubliclyVisibleSchema
455          */
456         // Merged list available from getPubliclyVisibleSchemas
457         @CanIgnoreReturnValue
458         @SuppressLint("MissingGetterMatchingBuilder")
459         @FlaggedApi(Flags.FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA)
460         @NonNull
setPubliclyVisibleSchema( @onNull String schemaType, @NonNull PackageIdentifier packageIdentifier)461         public Builder setPubliclyVisibleSchema(
462                 @NonNull String schemaType, @NonNull PackageIdentifier packageIdentifier) {
463             Objects.requireNonNull(schemaType);
464             Objects.requireNonNull(packageIdentifier);
465             resetIfBuilt();
466             InternalVisibilityConfig.Builder visibilityConfigBuilder =
467                     getOrCreateVisibilityConfigBuilder(schemaType);
468             visibilityConfigBuilder.setPubliclyVisibleTargetPackage(packageIdentifier);
469             return this;
470         }
471 
472         /**
473          * Sets the documents from the provided {@code schemaType} can be read by the caller if they
474          * match the ALL visibility requirements set in {@link SchemaVisibilityConfig}.
475          *
476          * <p>The requirements in a {@link SchemaVisibilityConfig} is "AND" relationship. A caller
477          * must match ALL requirements to access the schema. For example, a caller must hold
478          * required permissions AND it is a specified package.
479          *
480          * <p>The querier could have access if they match ALL requirements in ANY of the given
481          * {@link SchemaVisibilityConfig}s
482          *
483          * <p>For example, if the Set contains {@code {% verbatim %}{{PackageA and Permission1},
484          * {PackageB and Permission2}}{% endverbatim %}}.
485          *
486          * <ul>
487          *   <li>A querier from packageA could read if they holds Permission1.
488          *   <li>A querier from packageA could NOT read if they only holds Permission2 instead of
489          *       Permission1.
490          *   <li>A querier from packageB could read if they holds Permission2.
491          *   <li>A querier from packageC could never read.
492          *   <li>A querier holds both PermissionA and PermissionE has access.
493          * </ul>
494          *
495          * @param schemaType The schema type to set visibility on.
496          * @param visibleToConfigs The {@link SchemaVisibilityConfig}s hold all requirements that a
497          *     call must to match to access the schema.
498          */
499         // Merged map available from getSchemasVisibleToConfigs
500         @CanIgnoreReturnValue
501         @SuppressLint("MissingGetterMatchingBuilder")
502         @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
503         @NonNull
setSchemaTypeVisibleToConfigs( @onNull String schemaType, @NonNull Set<SchemaVisibilityConfig> visibleToConfigs)504         public Builder setSchemaTypeVisibleToConfigs(
505                 @NonNull String schemaType, @NonNull Set<SchemaVisibilityConfig> visibleToConfigs) {
506             Objects.requireNonNull(schemaType);
507             Objects.requireNonNull(visibleToConfigs);
508             resetIfBuilt();
509             InternalVisibilityConfig.Builder visibilityConfigBuilder =
510                     getOrCreateVisibilityConfigBuilder(schemaType);
511             for (SchemaVisibilityConfig visibleToConfig : visibleToConfigs) {
512                 visibilityConfigBuilder.addVisibleToConfig(visibleToConfig);
513             }
514             return this;
515         }
516 
517         /**
518          * Method to set visibility setting. If this is called with false, {@link
519          * #getRequiredPermissionsForSchemaTypeVisibility()}, {@link
520          * #getSchemaTypesNotDisplayedBySystem()}}, and {@link #getSchemaTypesVisibleToPackages()}
521          * calls will throw an {@link UnsupportedOperationException}. If called with true,
522          * visibility information for all schemas will be cleared.
523          *
524          * @param visibilitySettingSupported whether supported {@link
525          *     Features#ADD_PERMISSIONS_AND_GET_VISIBILITY} by this backend/Android API level.
526          * @hide
527          */
528         // Visibility setting is determined by SDK version, so it won't be needed in framework
529         @CanIgnoreReturnValue
530         @SuppressLint("MissingGetterMatchingBuilder")
531         @NonNull
setVisibilitySettingSupported(boolean visibilitySettingSupported)532         public Builder setVisibilitySettingSupported(boolean visibilitySettingSupported) {
533             if (visibilitySettingSupported) {
534                 mVisibilityConfigBuilders = new ArrayMap<>();
535             } else {
536                 mVisibilityConfigBuilders = null;
537             }
538             return this;
539         }
540 
541         /** Builds a {@link GetSchemaResponse} object. */
542         @NonNull
build()543         public GetSchemaResponse build() {
544             List<InternalVisibilityConfig> visibilityConfigs = null;
545             if (mVisibilityConfigBuilders != null) {
546                 visibilityConfigs = new ArrayList<>();
547                 for (InternalVisibilityConfig.Builder builder :
548                         mVisibilityConfigBuilders.values()) {
549                     visibilityConfigs.add(builder.build());
550                 }
551             }
552             mBuilt = true;
553             return new GetSchemaResponse(mVersion, mSchemas, visibilityConfigs);
554         }
555 
556         @NonNull
getOrCreateVisibilityConfigBuilder( @onNull String schemaType)557         private InternalVisibilityConfig.Builder getOrCreateVisibilityConfigBuilder(
558                 @NonNull String schemaType) {
559             if (mVisibilityConfigBuilders == null) {
560                 throw new IllegalStateException(
561                         "GetSchemaResponse is not configured with" + "visibility setting support");
562             }
563             InternalVisibilityConfig.Builder builder = mVisibilityConfigBuilders.get(schemaType);
564             if (builder == null) {
565                 builder = new InternalVisibilityConfig.Builder(schemaType);
566                 mVisibilityConfigBuilders.put(schemaType, builder);
567             }
568             return builder;
569         }
570 
resetIfBuilt()571         private void resetIfBuilt() {
572             if (mBuilt) {
573                 // No need to copy mVisibilityConfigBuilders -- it gets copied during build().
574                 mSchemas = new ArrayList<>(mSchemas);
575                 mBuilt = false;
576             }
577         }
578     }
579 }
580