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