1 /* 2 * Copyright 2020 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 21 import androidx.annotation.IntDef; 22 import androidx.annotation.IntRange; 23 import androidx.annotation.NonNull; 24 import androidx.annotation.Nullable; 25 import androidx.annotation.RequiresFeature; 26 import androidx.annotation.RestrictTo; 27 import androidx.appsearch.annotation.CanIgnoreReturnValue; 28 import androidx.appsearch.exceptions.AppSearchException; 29 import androidx.appsearch.flags.FlaggedApi; 30 import androidx.appsearch.flags.Flags; 31 import androidx.collection.ArrayMap; 32 import androidx.collection.ArraySet; 33 import androidx.core.util.ObjectsCompat; 34 import androidx.core.util.Preconditions; 35 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Collection; 41 import java.util.Collections; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Set; 45 46 /** 47 * Encapsulates a request to update the schema of an {@link AppSearchSession} database. 48 * 49 * <p>The schema is composed of a collection of {@link AppSearchSchema} objects, each of which 50 * defines a unique type of data. 51 * 52 * <p>The first call to SetSchemaRequest will set the provided schema and store it within the 53 * {@link AppSearchSession} database. 54 * 55 * <p>Subsequent calls will compare the provided schema to the previously saved schema, to 56 * determine how to treat existing documents. 57 * 58 * <p>The following types of schema modifications are always safe and are made without deleting any 59 * existing documents: 60 * <ul> 61 * <li>Addition of new {@link AppSearchSchema} types 62 * <li>Addition of new properties to an existing {@link AppSearchSchema} type 63 * <li>Changing the cardinality of a property to be less restrictive 64 * </ul> 65 * 66 * <p>The following types of schema changes are not backwards compatible: 67 * <ul> 68 * <li>Removal of an existing {@link AppSearchSchema} type 69 * <li>Removal of a property from an existing {@link AppSearchSchema} type 70 * <li>Changing the data type of an existing property 71 * <li>Changing the cardinality of a property to be more restrictive 72 * </ul> 73 * 74 * <p>Providing a schema with incompatible changes, will throw an 75 * {@link androidx.appsearch.exceptions.AppSearchException}, with a message describing the 76 * incompatibility. As a result, the previously set schema will remain unchanged. 77 * 78 * <p>Backward incompatible changes can be made by : 79 * <ul> 80 * <li>setting {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. 81 * This deletes all documents that are incompatible with the new schema. The new schema is 82 * then saved and persisted to disk. 83 * <li>Add a {@link Migrator} for each incompatible type and make no deletion. The migrator 84 * will migrate documents from its old schema version to the new version. Migrated types 85 * will be set into both {@link SetSchemaResponse#getIncompatibleTypes()} and 86 * {@link SetSchemaResponse#getMigratedTypes()}. See the migration section below. 87 * </ul> 88 * @see AppSearchSession#setSchemaAsync 89 * @see Migrator 90 */ 91 // TODO(b/384721898): Switch to JSpecify annotations 92 @SuppressWarnings("JSpecifyNullness") 93 public final class SetSchemaRequest { 94 95 /** 96 * List of Android Permission are supported in 97 * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility} 98 * 99 * @see android.Manifest.permission 100 * @exportToFramework:hide 101 */ 102 @IntDef(value = { 103 READ_SMS, 104 READ_CALENDAR, 105 READ_CONTACTS, 106 READ_EXTERNAL_STORAGE, 107 READ_HOME_APP_SEARCH_DATA, 108 READ_ASSISTANT_APP_SEARCH_DATA, 109 ENTERPRISE_ACCESS, 110 MANAGED_PROFILE_CONTACTS_ACCESS, 111 EXECUTE_APP_FUNCTIONS, 112 PACKAGE_USAGE_STATS, 113 }) 114 @Retention(RetentionPolicy.SOURCE) 115 @RequiresFeature( 116 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 117 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) 118 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 119 public @interface AppSearchSupportedPermission {} 120 121 /** 122 * The {@link android.Manifest.permission#READ_SMS} AppSearch supported in 123 * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility} 124 */ 125 @RequiresFeature( 126 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 127 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) 128 public static final int READ_SMS = 1; 129 130 /** 131 * The {@link android.Manifest.permission#READ_CALENDAR} AppSearch supported in 132 * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility} 133 */ 134 @RequiresFeature( 135 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 136 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) 137 public static final int READ_CALENDAR = 2; 138 139 /** 140 * The {@link android.Manifest.permission#READ_CONTACTS} AppSearch supported in 141 * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility} 142 */ 143 @RequiresFeature( 144 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 145 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) 146 public static final int READ_CONTACTS = 3; 147 148 /** 149 * The {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} AppSearch supported in 150 * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility} 151 */ 152 @RequiresFeature( 153 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 154 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) 155 public static final int READ_EXTERNAL_STORAGE = 4; 156 157 /** 158 * The {@link android.Manifest.permission#READ_HOME_APP_SEARCH_DATA} AppSearch supported in 159 * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility} 160 */ 161 @RequiresFeature( 162 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 163 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) 164 public static final int READ_HOME_APP_SEARCH_DATA = 5; 165 166 /** 167 * The {@link android.Manifest.permission#READ_ASSISTANT_APP_SEARCH_DATA} AppSearch supported in 168 * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility} 169 */ 170 @RequiresFeature( 171 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 172 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) 173 public static final int READ_ASSISTANT_APP_SEARCH_DATA = 6; 174 175 /** 176 * A schema must have this permission set through {@link 177 * SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility} to be visible to an 178 * {@link EnterpriseGlobalSearchSession}. A call from a regular {@link GlobalSearchSession} will 179 * not count as having this permission. 180 * 181 * @exportToFramework:hide 182 */ 183 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 184 public static final int ENTERPRISE_ACCESS = 7; 185 186 /** 187 * A schema with this permission set through {@link 188 * SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility} requires the caller 189 * to have managed profile contacts access from {@link android.app.admin.DevicePolicyManager} to 190 * be visible. This permission indicates that the protected schema may expose managed profile 191 * data for contacts search. 192 * 193 * @exportToFramework:hide 194 */ 195 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 196 public static final int MANAGED_PROFILE_CONTACTS_ACCESS = 8; 197 198 /** 199 * The AppSearch enumeration corresponding to {@link 200 * android.Manifest.permission#EXECUTE_APP_FUNCTIONS} Android permission that can be used to 201 * guard AppSearch schema type visibility in {@link 202 * SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility}. 203 * 204 * <p>This is internally used by AppFunctions API to store app functions runtime metadata so it 205 * is visible to packages holding {@link android.Manifest.permission#EXECUTE_APP_FUNCTIONS} 206 * permission (currently associated with system assistant apps). 207 * 208 * @exportToFramework:hide 209 */ 210 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 211 public static final int EXECUTE_APP_FUNCTIONS = 9; 212 213 /** 214 * @deprecated The corresponding permission is deprecated. Some documents are already persisted 215 * with this constant, therefore keeping the constant here for compatibility reasons. 216 * 217 * @exportToFramework:hide 218 */ 219 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 220 public static final int EXECUTE_APP_FUNCTIONS_TRUSTED = 10; 221 222 /** 223 * The {@link android.Manifest.permission#PACKAGE_USAGE_STATS} AppSearch supported in {@link 224 * SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility} 225 * 226 * @exportToFramework:hide 227 */ 228 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 229 public static final int PACKAGE_USAGE_STATS = 11; 230 231 private final Set<AppSearchSchema> mSchemas; 232 private final Set<String> mSchemasNotDisplayedBySystem; 233 private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages; 234 private final Map<String, Set<Set<Integer>>> mSchemasVisibleToPermissions; 235 private final Map<String, PackageIdentifier> mPubliclyVisibleSchemas; 236 private final Map<String, Set<SchemaVisibilityConfig>> mSchemasVisibleToConfigs; 237 private final Map<String, Migrator> mMigrators; 238 private final boolean mForceOverride; 239 private final int mVersion; 240 SetSchemaRequest(@onNull Set<AppSearchSchema> schemas, @NonNull Set<String> schemasNotDisplayedBySystem, @NonNull Map<String, Set<PackageIdentifier>> schemasVisibleToPackages, @NonNull Map<String, Set<Set<Integer>>> schemasVisibleToPermissions, @NonNull Map<String, PackageIdentifier> publiclyVisibleSchemas, @NonNull Map<String, Set<SchemaVisibilityConfig>> schemasVisibleToConfigs, @NonNull Map<String, Migrator> migrators, boolean forceOverride, int version)241 SetSchemaRequest(@NonNull Set<AppSearchSchema> schemas, 242 @NonNull Set<String> schemasNotDisplayedBySystem, 243 @NonNull Map<String, Set<PackageIdentifier>> schemasVisibleToPackages, 244 @NonNull Map<String, Set<Set<Integer>>> schemasVisibleToPermissions, 245 @NonNull Map<String, PackageIdentifier> publiclyVisibleSchemas, 246 @NonNull Map<String, Set<SchemaVisibilityConfig>> schemasVisibleToConfigs, 247 @NonNull Map<String, Migrator> migrators, 248 boolean forceOverride, 249 int version) { 250 mSchemas = Preconditions.checkNotNull(schemas); 251 mSchemasNotDisplayedBySystem = Preconditions.checkNotNull(schemasNotDisplayedBySystem); 252 mSchemasVisibleToPackages = Preconditions.checkNotNull(schemasVisibleToPackages); 253 mSchemasVisibleToPermissions = Preconditions.checkNotNull(schemasVisibleToPermissions); 254 mPubliclyVisibleSchemas = Preconditions.checkNotNull(publiclyVisibleSchemas); 255 mSchemasVisibleToConfigs = Preconditions.checkNotNull(schemasVisibleToConfigs); 256 mMigrators = Preconditions.checkNotNull(migrators); 257 mForceOverride = forceOverride; 258 mVersion = version; 259 } 260 261 /** Returns the {@link AppSearchSchema} types that are part of this request. */ getSchemas()262 public @NonNull Set<AppSearchSchema> getSchemas() { 263 return Collections.unmodifiableSet(mSchemas); 264 } 265 266 /** 267 * Returns all the schema types that are opted out of being displayed and visible on any 268 * system UI surface. 269 */ getSchemasNotDisplayedBySystem()270 public @NonNull Set<String> getSchemasNotDisplayedBySystem() { 271 return Collections.unmodifiableSet(mSchemasNotDisplayedBySystem); 272 } 273 274 /** 275 * Returns a mapping of schema types to the set of packages that have access 276 * to that schema type. 277 * 278 * <p>It’s inefficient to call this method repeatedly. 279 */ getSchemasVisibleToPackages()280 public @NonNull Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackages() { 281 Map<String, Set<PackageIdentifier>> copy = new ArrayMap<>(); 282 for (Map.Entry<String, Set<PackageIdentifier>> entry : 283 mSchemasVisibleToPackages.entrySet()) { 284 copy.put(entry.getKey(), new ArraySet<>(entry.getValue())); 285 } 286 return copy; 287 } 288 289 /** 290 * Returns a mapping of schema types to the Map of {@link android.Manifest.permission} 291 * combinations that querier must hold to access that schema type. 292 * 293 * <p> The querier could read the {@link GenericDocument} objects under the {@code schemaType} 294 * if they holds ALL required permissions of ANY of the individual value sets. 295 * 296 * <p>For example, if the Map contains {@code {% verbatim %}{{permissionA, PermissionB}, 297 * {PermissionC, PermissionD}, {PermissionE}}{% endverbatim %}}. 298 * <ul> 299 * <li>A querier holds both PermissionA and PermissionB has access.</li> 300 * <li>A querier holds both PermissionC and PermissionD has access.</li> 301 * <li>A querier holds only PermissionE has access.</li> 302 * <li>A querier holds both PermissionA and PermissionE has access.</li> 303 * <li>A querier holds only PermissionA doesn't have access.</li> 304 * <li>A querier holds both PermissionA and PermissionC doesn't have access.</li> 305 * </ul> 306 * 307 * <p>It’s inefficient to call this method repeatedly. 308 * 309 * @return The map contains schema type and all combinations of required permission for querier 310 * to access it. The supported Permission are {@link SetSchemaRequest#READ_SMS}, 311 * {@link SetSchemaRequest#READ_CALENDAR}, {@link SetSchemaRequest#READ_CONTACTS}, 312 * {@link SetSchemaRequest#READ_EXTERNAL_STORAGE}, 313 * {@link SetSchemaRequest#READ_HOME_APP_SEARCH_DATA} and 314 * {@link SetSchemaRequest#READ_ASSISTANT_APP_SEARCH_DATA}. 315 */ 316 // TODO(b/237388235): add enterprise permissions to javadocs after they're unhidden 317 // Annotation is here to suppress lint error. Lint error is erroneous since the method does not 318 // require the caller to hold any permission for the method to function. 319 @SuppressLint("RequiresPermission") getRequiredPermissionsForSchemaTypeVisibility()320 public @NonNull Map<String, Set<Set<Integer>>> getRequiredPermissionsForSchemaTypeVisibility() { 321 return deepCopy(mSchemasVisibleToPermissions); 322 } 323 324 /** 325 * Returns a mapping of publicly visible schemas to the {@link PackageIdentifier} specifying 326 * the package the schemas are from. 327 */ 328 @FlaggedApi(Flags.FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA) getPubliclyVisibleSchemas()329 public @NonNull Map<String, PackageIdentifier> getPubliclyVisibleSchemas() { 330 return Collections.unmodifiableMap(mPubliclyVisibleSchemas); 331 } 332 333 /** 334 * Returns a mapping of schema types to the set of {@link SchemaVisibilityConfig} that have 335 * access to that schema type. 336 * 337 * <p>It’s inefficient to call this method repeatedly. 338 * @see SetSchemaRequest.Builder#addSchemaTypeVisibleToConfig 339 */ 340 @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS) getSchemasVisibleToConfigs()341 public @NonNull Map<String, Set<SchemaVisibilityConfig>> getSchemasVisibleToConfigs() { 342 Map<String, Set<SchemaVisibilityConfig>> copy = new ArrayMap<>(); 343 for (Map.Entry<String, Set<SchemaVisibilityConfig>> entry : 344 mSchemasVisibleToConfigs.entrySet()) { 345 copy.put(entry.getKey(), new ArraySet<>(entry.getValue())); 346 } 347 return copy; 348 } 349 350 /** 351 * Returns the map of {@link Migrator}, the key will be the schema type of the 352 * {@link Migrator} associated with. 353 */ getMigrators()354 public @NonNull Map<String, Migrator> getMigrators() { 355 return Collections.unmodifiableMap(mMigrators); 356 } 357 358 /** 359 * Returns a mapping of {@link AppSearchSchema} types to the set of packages that have access 360 * to that schema type. 361 * 362 * <p>A more efficient version of {@link #getSchemasVisibleToPackages}, but it returns a 363 * modifiable map. This is not meant to be unhidden and should only be used by internal 364 * classes. 365 * 366 * @exportToFramework:hide 367 */ 368 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) getSchemasVisibleToPackagesInternal()369 public @NonNull Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackagesInternal() { 370 return mSchemasVisibleToPackages; 371 } 372 373 /** Returns whether this request will force the schema to be overridden. */ isForceOverride()374 public boolean isForceOverride() { 375 return mForceOverride; 376 } 377 378 /** Returns the database overall schema version. */ 379 @IntRange(from = 1) getVersion()380 public int getVersion() { 381 return mVersion; 382 } 383 384 @Override equals(@ullable Object other)385 public boolean equals(@Nullable Object other) { 386 if (this == other) { 387 return true; 388 } 389 if (!(other instanceof SetSchemaRequest)) { 390 return false; 391 } 392 SetSchemaRequest otherRequest = (SetSchemaRequest) other; 393 return mSchemas.equals(otherRequest.mSchemas) 394 && mSchemasNotDisplayedBySystem.equals(otherRequest.mSchemasNotDisplayedBySystem) 395 && mSchemasVisibleToPackages.equals(otherRequest.mSchemasVisibleToPackages) 396 && mSchemasVisibleToPermissions.equals(otherRequest.mSchemasVisibleToPermissions) 397 && mPubliclyVisibleSchemas.equals(otherRequest.mPubliclyVisibleSchemas) 398 && mSchemasVisibleToConfigs.equals(otherRequest.mSchemasVisibleToConfigs) 399 && mMigrators.equals(otherRequest.mMigrators) 400 && mForceOverride == otherRequest.mForceOverride 401 && mVersion == otherRequest.mVersion; 402 } 403 404 @Override hashCode()405 public int hashCode() { 406 return ObjectsCompat.hash(mSchemas, mSchemasNotDisplayedBySystem, mSchemasVisibleToPackages, 407 mSchemasVisibleToPermissions, mPubliclyVisibleSchemas, mSchemasVisibleToConfigs, mMigrators, 408 mForceOverride, mVersion); 409 } 410 411 /** Builder for {@link SetSchemaRequest} objects. */ 412 public static final class Builder { 413 private static final int DEFAULT_VERSION = 1; 414 private ArraySet<AppSearchSchema> mSchemas = new ArraySet<>(); 415 private ArraySet<String> mSchemasNotDisplayedBySystem = new ArraySet<>(); 416 private ArrayMap<String, Set<PackageIdentifier>> mSchemasVisibleToPackages = 417 new ArrayMap<>(); 418 private ArrayMap<String, Set<Set<Integer>>> mSchemasVisibleToPermissions = new ArrayMap<>(); 419 private ArrayMap<String, PackageIdentifier> mPubliclyVisibleSchemas = new ArrayMap<>(); 420 private ArrayMap<String, Set<SchemaVisibilityConfig>> mSchemaVisibleToConfigs = 421 new ArrayMap<>(); 422 private ArrayMap<String, Migrator> mMigrators = new ArrayMap<>(); 423 private boolean mForceOverride = false; 424 private int mVersion = DEFAULT_VERSION; 425 private boolean mBuilt = false; 426 427 /** Creates a new {@link SetSchemaRequest.Builder}. */ Builder()428 public Builder() { 429 } 430 431 /** 432 * Creates a {@link SetSchemaRequest.Builder} from the given {@link SetSchemaRequest}. 433 */ 434 @ExperimentalAppSearchApi 435 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) Builder(@onNull SetSchemaRequest request)436 public Builder(@NonNull SetSchemaRequest request) { 437 mSchemas.addAll(request.mSchemas); 438 mSchemasNotDisplayedBySystem.addAll(request.mSchemasNotDisplayedBySystem); 439 for (Map.Entry<String, Set<PackageIdentifier>> entry 440 : request.mSchemasVisibleToPackages.entrySet()) { 441 mSchemasVisibleToPackages.put(entry.getKey(), new ArraySet<>(entry.getValue())); 442 } 443 mSchemasVisibleToPermissions = deepCopy(request.mSchemasVisibleToPermissions); 444 mPubliclyVisibleSchemas.putAll(request.mPubliclyVisibleSchemas); 445 for (Map.Entry<String, Set<SchemaVisibilityConfig>> entry : 446 request.mSchemasVisibleToConfigs.entrySet()) { 447 mSchemaVisibleToConfigs.put(entry.getKey(), new ArraySet<>(entry.getValue())); 448 } 449 mMigrators.putAll(request.mMigrators); 450 mForceOverride = request.mForceOverride; 451 mVersion = request.mVersion; 452 } 453 454 /** 455 * Adds one or more {@link AppSearchSchema} types to the schema. 456 * 457 * <p>An {@link AppSearchSchema} object represents one type of structured data. 458 * 459 * <p>Any documents of these types will be displayed on system UI surfaces by default. 460 */ 461 @CanIgnoreReturnValue addSchemas(@onNull AppSearchSchema... schemas)462 public @NonNull Builder addSchemas(@NonNull AppSearchSchema... schemas) { 463 Preconditions.checkNotNull(schemas); 464 resetIfBuilt(); 465 return addSchemas(Arrays.asList(schemas)); 466 } 467 468 /** 469 * Adds a collection of {@link AppSearchSchema} objects to the schema. 470 * 471 * <p>An {@link AppSearchSchema} object represents one type of structured data. 472 */ 473 @CanIgnoreReturnValue addSchemas(@onNull Collection<AppSearchSchema> schemas)474 public @NonNull Builder addSchemas(@NonNull Collection<AppSearchSchema> schemas) { 475 Preconditions.checkNotNull(schemas); 476 resetIfBuilt(); 477 mSchemas.addAll(schemas); 478 return this; 479 } 480 481 // @exportToFramework:startStrip() 482 /** 483 * Adds one or more {@link androidx.appsearch.annotation.Document} annotated classes to the 484 * schema. 485 * 486 * <p>Merged list available from {@link #getSchemas()}. 487 * 488 * @param documentClasses classes annotated with 489 * {@link androidx.appsearch.annotation.Document}. 490 * @throws AppSearchException if {@code androidx.appsearch.compiler.AppSearchCompiler} 491 * has not generated a schema for the given document classes. 492 */ 493 @CanIgnoreReturnValue 494 @SuppressLint("MissingGetterMatchingBuilder") addDocumentClasses(@onNull Class<?>.... documentClasses)495 public @NonNull Builder addDocumentClasses(@NonNull Class<?>... documentClasses) 496 throws AppSearchException { 497 Preconditions.checkNotNull(documentClasses); 498 resetIfBuilt(); 499 return addDocumentClasses(Arrays.asList(documentClasses)); 500 } 501 502 /** 503 * Adds a collection of {@link androidx.appsearch.annotation.Document} annotated classes to 504 * the schema. 505 * 506 * <p>This will also add all {@link androidx.appsearch.annotation.Document} classes 507 * referenced by the schema via document properties. 508 * 509 * <p>Merged list available from {@link #getSchemas()}. 510 * 511 * @param documentClasses classes annotated with 512 * {@link androidx.appsearch.annotation.Document}. 513 * @throws AppSearchException if {@code androidx.appsearch.compiler.AppSearchCompiler} 514 * has not generated a schema for the given document classes. 515 */ 516 @CanIgnoreReturnValue 517 @SuppressLint("MissingGetterMatchingBuilder") addDocumentClasses( @onNull Collection<? extends Class<?>> documentClasses)518 public @NonNull Builder addDocumentClasses( 519 @NonNull Collection<? extends Class<?>> documentClasses) throws AppSearchException { 520 Preconditions.checkNotNull(documentClasses); 521 resetIfBuilt(); 522 523 DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance(); 524 525 List<Class<?>> processedClasses = new ArrayList<>(documentClasses.size()); 526 processedClasses.addAll(documentClasses); 527 528 for (int i = 0; i < processedClasses.size(); i++) { 529 DocumentClassFactory<?> factory = 530 registry.getOrCreateFactory(processedClasses.get(i)); 531 for (Class<?> nested: factory.getDependencyDocumentClasses()) { 532 if (!processedClasses.contains(nested)) { 533 processedClasses.add(nested); 534 } 535 } 536 } 537 538 List<AppSearchSchema> schemas = new ArrayList<>(processedClasses.size()); 539 for (Class<?> documentClass : processedClasses) { 540 DocumentClassFactory<?> factory = 541 registry.getOrCreateFactory(documentClass); 542 schemas.add(factory.getSchema()); 543 } 544 545 return addSchemas(schemas); 546 } 547 // @exportToFramework:endStrip() 548 549 /** 550 * Clears all {@link AppSearchSchema}s from the list of schemas. 551 */ 552 @ExperimentalAppSearchApi 553 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 554 @CanIgnoreReturnValue clearSchemas()555 public @NonNull Builder clearSchemas() { 556 resetIfBuilt(); 557 mSchemas.clear(); 558 return this; 559 } 560 561 /** 562 * Sets whether or not documents from the provided {@code schemaType} will be displayed 563 * and visible on any system UI surface. 564 * 565 * <p>This setting applies to the provided {@code schemaType} only, and does not persist 566 * across {@link AppSearchSession#setSchemaAsync} calls. 567 * 568 * <p>The default behavior, if this method is not called, is to allow types to be 569 * displayed on system UI surfaces. 570 * 571 * @param schemaType The name of an {@link AppSearchSchema} within the same 572 * {@link SetSchemaRequest}, which will be configured. 573 * @param displayed Whether documents of this type will be displayed on system UI surfaces. 574 */ 575 // Merged list available from getSchemasNotDisplayedBySystem 576 @CanIgnoreReturnValue 577 @SuppressLint("MissingGetterMatchingBuilder") setSchemaTypeDisplayedBySystem( @onNull String schemaType, boolean displayed)578 public @NonNull Builder setSchemaTypeDisplayedBySystem( 579 @NonNull String schemaType, boolean displayed) { 580 Preconditions.checkNotNull(schemaType); 581 resetIfBuilt(); 582 if (displayed) { 583 mSchemasNotDisplayedBySystem.remove(schemaType); 584 } else { 585 mSchemasNotDisplayedBySystem.add(schemaType); 586 } 587 return this; 588 } 589 590 /** 591 * Adds a set of required Android {@link android.Manifest.permission} combination to the 592 * given schema type. 593 * 594 * <p> If the querier holds ALL of the required permissions in this combination, they will 595 * have access to read {@link GenericDocument} objects of the given schema type. 596 * 597 * <p> You can call this method to add multiple permission combinations, and the querier 598 * will have access if they holds ANY of the combinations. 599 * 600 * <p> The supported Permissions are {@link #READ_SMS}, {@link #READ_CALENDAR}, 601 * {@link #READ_CONTACTS}, {@link #READ_EXTERNAL_STORAGE}, 602 * {@link #READ_HOME_APP_SEARCH_DATA} and {@link #READ_ASSISTANT_APP_SEARCH_DATA}. 603 * 604 * <p> The relationship between permissions added in this method and package visibility 605 * setting {@link #setSchemaTypeVisibilityForPackage} is "OR". The caller could access 606 * the schema if they match ANY requirements. If you want to set "AND" requirements like 607 * a caller must hold required permissions AND it is a specified package, please use 608 * {@link #addSchemaTypeVisibleToConfig}. 609 * 610 * @see android.Manifest.permission#READ_SMS 611 * @see android.Manifest.permission#READ_CALENDAR 612 * @see android.Manifest.permission#READ_CONTACTS 613 * @see android.Manifest.permission#READ_EXTERNAL_STORAGE 614 * @see android.Manifest.permission#READ_HOME_APP_SEARCH_DATA 615 * @see android.Manifest.permission#READ_ASSISTANT_APP_SEARCH_DATA 616 * @param schemaType The schema type to set visibility on. 617 * @param permissions A set of required Android permissions the caller need to hold 618 * to access {@link GenericDocument} objects that under the given 619 * schema. 620 * @throws IllegalArgumentException – if input unsupported permission. 621 */ 622 // TODO(b/237388235): add enterprise permissions to javadocs after they're unhidden 623 // Merged list available from getRequiredPermissionsForSchemaTypeVisibility 624 // Annotation is here to suppress lint error. Lint error is erroneous since the method does 625 // not require the caller to hold any permission for the method to function. 626 @CanIgnoreReturnValue 627 @SuppressLint({"MissingGetterMatchingBuilder", "RequiresPermission"}) 628 @RequiresFeature( 629 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 630 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) addRequiredPermissionsForSchemaTypeVisibility( @onNull String schemaType, @AppSearchSupportedPermission @NonNull Set<Integer> permissions)631 public @NonNull Builder addRequiredPermissionsForSchemaTypeVisibility( 632 @NonNull String schemaType, 633 @AppSearchSupportedPermission @NonNull Set<Integer> permissions) { 634 Preconditions.checkNotNull(schemaType); 635 Preconditions.checkNotNull(permissions); 636 for (int permission : permissions) { 637 Preconditions.checkArgumentInRange(permission, READ_SMS, 638 PACKAGE_USAGE_STATS, "permission"); 639 } 640 resetIfBuilt(); 641 Set<Set<Integer>> visibleToPermissions = mSchemasVisibleToPermissions.get(schemaType); 642 if (visibleToPermissions == null) { 643 visibleToPermissions = new ArraySet<>(); 644 mSchemasVisibleToPermissions.put(schemaType, visibleToPermissions); 645 } 646 visibleToPermissions.add(permissions); 647 return this; 648 } 649 650 /** Clears all required permissions combinations for the given schema type. */ 651 @CanIgnoreReturnValue 652 @RequiresFeature( 653 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 654 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) clearRequiredPermissionsForSchemaTypeVisibility( @onNull String schemaType)655 public @NonNull Builder clearRequiredPermissionsForSchemaTypeVisibility( 656 @NonNull String schemaType) { 657 Preconditions.checkNotNull(schemaType); 658 resetIfBuilt(); 659 mSchemasVisibleToPermissions.remove(schemaType); 660 return this; 661 } 662 663 /** 664 * Sets whether or not documents from the provided {@code schemaType} can be read by the 665 * specified package. 666 * 667 * <p>Each package is represented by a {@link PackageIdentifier}, containing a package name 668 * and a byte array of type {@link android.content.pm.PackageManager#CERT_INPUT_SHA256}. 669 * 670 * <p>To opt into one-way data sharing with another application, the developer will need to 671 * explicitly grant the other application’s package name and certificate Read access to its 672 * data. 673 * 674 * <p>For two-way data sharing, both applications need to explicitly grant Read access to 675 * one another. 676 * 677 * <p>By default, data sharing between applications is disabled. 678 * 679 * <p> The relationship between permissions added in this method and package visibility 680 * setting {@link #setSchemaTypeVisibilityForPackage} is "OR". The caller could access 681 * the schema if they match ANY requirements. If you want to set "AND" requirements like 682 * a caller must hold required permissions AND it is a specified package, please use 683 * {@link #addSchemaTypeVisibleToConfig}. 684 * 685 * @param schemaType The schema type to set visibility on. 686 * @param visible Whether the {@code schemaType} will be visible or not. 687 * @param packageIdentifier Represents the package that will be granted visibility. 688 */ 689 // Merged list available from getSchemasVisibleToPackages 690 @CanIgnoreReturnValue 691 @SuppressLint("MissingGetterMatchingBuilder") setSchemaTypeVisibilityForPackage( @onNull String schemaType, boolean visible, @NonNull PackageIdentifier packageIdentifier)692 public @NonNull Builder setSchemaTypeVisibilityForPackage( 693 @NonNull String schemaType, 694 boolean visible, 695 @NonNull PackageIdentifier packageIdentifier) { 696 Preconditions.checkNotNull(schemaType); 697 Preconditions.checkNotNull(packageIdentifier); 698 resetIfBuilt(); 699 700 Set<PackageIdentifier> packageIdentifiers = mSchemasVisibleToPackages.get(schemaType); 701 if (visible) { 702 if (packageIdentifiers == null) { 703 packageIdentifiers = new ArraySet<>(); 704 } 705 packageIdentifiers.add(packageIdentifier); 706 mSchemasVisibleToPackages.put(schemaType, packageIdentifiers); 707 } else { 708 if (packageIdentifiers == null) { 709 // Return early since there was nothing set to begin with. 710 return this; 711 } 712 packageIdentifiers.remove(packageIdentifier); 713 if (packageIdentifiers.isEmpty()) { 714 // Remove the entire key so that we don't have empty sets as values. 715 mSchemasVisibleToPackages.remove(schemaType); 716 } 717 } 718 719 return this; 720 } 721 722 /** 723 * Specify that the schema should be publicly available, to packages which already have 724 * visibility to {@code packageIdentifier}. This visibility is determined by the result of 725 * {@link android.content.pm.PackageManager#canPackageQuery}. 726 * 727 * <p> It is possible for the packageIdentifier parameter to be different from the 728 * package performing the indexing. This might happen in the case of an on-device indexer 729 * processing information about various packages. The visibility will be the same 730 * regardless of which package indexes the document, as the visibility is based on the 731 * packageIdentifier parameter. 732 * 733 * <p> If this is called repeatedly with the same schema, the {@link PackageIdentifier} in 734 * the last call will be used as the "from" package for that schema. 735 * 736 * <p> Calling this with packageIdentifier set to null is valid, and will remove public 737 * visibility for the schema. 738 * 739 * @param schema the schema to make publicly accessible. 740 * @param packageIdentifier if an app can see this package via 741 * PackageManager#canPackageQuery, it will be able to see the 742 * documents of type {@code schema}. 743 */ 744 // Merged list available from getPubliclyVisibleSchemas 745 @CanIgnoreReturnValue 746 @SuppressLint("MissingGetterMatchingBuilder") 747 @RequiresFeature( 748 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 749 name = Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE) 750 @FlaggedApi(Flags.FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA) setPubliclyVisibleSchema(@onNull String schema, @Nullable PackageIdentifier packageIdentifier)751 public @NonNull Builder setPubliclyVisibleSchema(@NonNull String schema, 752 @Nullable PackageIdentifier packageIdentifier) { 753 Preconditions.checkNotNull(schema); 754 resetIfBuilt(); 755 756 // If the package identifier is null or empty we clear public visibility 757 if (packageIdentifier == null || packageIdentifier.getPackageName().isEmpty()) { 758 mPubliclyVisibleSchemas.remove(schema); 759 return this; 760 } 761 762 mPubliclyVisibleSchemas.put(schema, packageIdentifier); 763 return this; 764 } 765 766 // @exportToFramework:startStrip() 767 /** 768 * Specify that the documents from the provided 769 * {@link androidx.appsearch.annotation.Document} annotated class should be publicly 770 * available, to packages which already have visibility to {@code packageIdentifier}. This 771 * visibility is determined by the result of 772 * {@link android.content.pm.PackageManager#canPackageQuery}. 773 * 774 * <p> It is possible for the packageIdentifier parameter to be different from the 775 * package performing the indexing. This might happen in the case of an on-device indexer 776 * processing information about various packages. The visibility will be the same 777 * regardless of which package indexes the document, as the visibility is based on the 778 * packageIdentifier parameter. 779 * 780 * <p> If this is called repeatedly with the same 781 * {@link androidx.appsearch.annotation.Document} annotated class, the 782 * {@link PackageIdentifier} in the last call will be used as the "from" package for that 783 * class (or schema). 784 * 785 * <p> Calling this with packageIdentifier set to null is valid, and will remove public 786 * visibility for the class (or schema). 787 * 788 * @param documentClass the {@link androidx.appsearch.annotation.Document} annotated class 789 * to make publicly accessible. 790 * @param packageIdentifier if an app can see this package via 791 * PackageManager#canPackageQuery, it will be able to see the 792 * documents of type {@code documentClass}. 793 * @see SetSchemaRequest.Builder#setPubliclyVisibleSchema 794 */ 795 // Merged list available from getPubliclyVisibleSchemas 796 @CanIgnoreReturnValue 797 @SuppressLint("MissingGetterMatchingBuilder") 798 @RequiresFeature( 799 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 800 name = Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE) 801 @FlaggedApi(Flags.FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA) setPubliclyVisibleDocumentClass(@onNull Class<?> documentClass, @Nullable PackageIdentifier packageIdentifier)802 public @NonNull Builder setPubliclyVisibleDocumentClass(@NonNull Class<?> documentClass, 803 @Nullable PackageIdentifier packageIdentifier) throws AppSearchException { 804 Preconditions.checkNotNull(documentClass); 805 resetIfBuilt(); 806 DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance(); 807 DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass); 808 return setPubliclyVisibleSchema(factory.getSchemaName(), packageIdentifier); 809 } 810 // @exportToFramework:endStrip() 811 812 /** 813 * Sets the documents from the provided {@code schemaType} can be read by the caller if they 814 * match the ALL visibility requirements set in {@link SchemaVisibilityConfig}. 815 * 816 * <p> The requirements in a {@link SchemaVisibilityConfig} is "AND" relationship. A 817 * caller must match ALL requirements to access the schema. For example, a caller must hold 818 * required permissions AND it is a specified package. 819 * 820 * <p> You can call this method repeatedly to add multiple {@link SchemaVisibilityConfig}s, 821 * and the querier will have access if they match ANY of the 822 * {@link SchemaVisibilityConfig}. 823 * 824 * @param schemaType The schema type to set visibility on. 825 * @param schemaVisibilityConfig The {@link SchemaVisibilityConfig} holds all requirements 826 * that a call must to match to access the schema. 827 */ 828 // Merged list available from getSchemasVisibleToConfigs 829 @CanIgnoreReturnValue 830 @SuppressLint("MissingGetterMatchingBuilder") 831 @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS) 832 @RequiresFeature( 833 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 834 name = Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG) addSchemaTypeVisibleToConfig(@onNull String schemaType, @NonNull SchemaVisibilityConfig schemaVisibilityConfig)835 public @NonNull Builder addSchemaTypeVisibleToConfig(@NonNull String schemaType, 836 @NonNull SchemaVisibilityConfig schemaVisibilityConfig) { 837 Preconditions.checkNotNull(schemaType); 838 Preconditions.checkNotNull(schemaVisibilityConfig); 839 resetIfBuilt(); 840 Set<SchemaVisibilityConfig> visibleToConfigs = mSchemaVisibleToConfigs.get(schemaType); 841 if (visibleToConfigs == null) { 842 visibleToConfigs = new ArraySet<>(); 843 mSchemaVisibleToConfigs.put(schemaType, visibleToConfigs); 844 } 845 visibleToConfigs.add(schemaVisibilityConfig); 846 return this; 847 } 848 849 /** Clears all visible to {@link SchemaVisibilityConfig} for the given schema type. */ 850 @CanIgnoreReturnValue 851 @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS) 852 @RequiresFeature( 853 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 854 name = Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG) clearSchemaTypeVisibleToConfigs(@onNull String schemaType)855 public @NonNull Builder clearSchemaTypeVisibleToConfigs(@NonNull String schemaType) { 856 Preconditions.checkNotNull(schemaType); 857 resetIfBuilt(); 858 mSchemaVisibleToConfigs.remove(schemaType); 859 return this; 860 } 861 862 /** 863 * Sets the {@link Migrator} associated with the given SchemaType. 864 * 865 * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type 866 * from the current version number stored in AppSearch to the final version set via 867 * {@link #setVersion}. 868 * 869 * <p>A {@link Migrator} will be invoked if the current version number stored in 870 * AppSearch is different from the final version set via {@link #setVersion} and 871 * {@link Migrator#shouldMigrate} returns {@code true}. 872 * 873 * <p>The target schema type of the output {@link GenericDocument} of 874 * {@link Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this 875 * {@link SetSchemaRequest}. 876 * 877 * @param schemaType The schema type to set migrator on. 878 * @param migrator The migrator translates a document from its current version to the 879 * final version set via {@link #setVersion}. 880 * 881 * @see SetSchemaRequest.Builder#setVersion 882 * @see SetSchemaRequest.Builder#addSchemas 883 * @see AppSearchSession#setSchemaAsync 884 */ 885 @CanIgnoreReturnValue 886 @SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects. setMigrator(@onNull String schemaType, @NonNull Migrator migrator)887 public @NonNull Builder setMigrator(@NonNull String schemaType, 888 @NonNull Migrator migrator) { 889 Preconditions.checkNotNull(schemaType); 890 Preconditions.checkNotNull(migrator); 891 resetIfBuilt(); 892 mMigrators.put(schemaType, migrator); 893 return this; 894 } 895 896 /** 897 * Sets a Map of {@link Migrator}s. 898 * 899 * <p>The key of the map is the schema type that the {@link Migrator} value applies to. 900 * 901 * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type 902 * from the current version number stored in AppSearch to the final version set via 903 * {@link #setVersion}. 904 * 905 * <p>A {@link Migrator} will be invoked if the current version number stored in 906 * AppSearch is different from the final version set via {@link #setVersion} and 907 * {@link Migrator#shouldMigrate} returns {@code true}. 908 * 909 * <p>The target schema type of the output {@link GenericDocument} of 910 * {@link Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this 911 * {@link SetSchemaRequest}. 912 * 913 * @param migrators A {@link Map} of migrators that translate a document from its current 914 * version to the final version set via {@link #setVersion}. The key of 915 * the map is the schema type that the {@link Migrator} value applies to. 916 * 917 * @see SetSchemaRequest.Builder#setVersion 918 * @see SetSchemaRequest.Builder#addSchemas 919 * @see AppSearchSession#setSchemaAsync 920 */ 921 @CanIgnoreReturnValue setMigrators(@onNull Map<String, Migrator> migrators)922 public @NonNull Builder setMigrators(@NonNull Map<String, Migrator> migrators) { 923 Preconditions.checkNotNull(migrators); 924 resetIfBuilt(); 925 mMigrators.putAll(migrators); 926 return this; 927 } 928 929 /** 930 * Clears all {@link Migrator}s. 931 */ 932 @ExperimentalAppSearchApi 933 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 934 @CanIgnoreReturnValue clearMigrators()935 public @NonNull Builder clearMigrators() { 936 resetIfBuilt(); 937 mMigrators.clear(); 938 return this; 939 } 940 941 // @exportToFramework:startStrip() 942 943 /** 944 * Sets whether or not documents from the provided 945 * {@link androidx.appsearch.annotation.Document} annotated class will be displayed and 946 * visible on any system UI surface. 947 * 948 * <p>This setting applies to the provided {@link androidx.appsearch.annotation.Document} 949 * annotated class only, and does not persist across {@link AppSearchSession#setSchemaAsync} 950 * calls. 951 * 952 * <p>The default behavior, if this method is not called, is to allow types to be 953 * displayed on system UI surfaces. 954 * 955 * <p> Merged list available from {@link #getSchemasNotDisplayedBySystem()}. 956 * 957 * @param documentClass A class annotated with 958 * {@link androidx.appsearch.annotation.Document}, the visibility of 959 * which will be configured 960 * @param displayed Whether documents of this type will be displayed on system UI 961 * surfaces. 962 * @throws AppSearchException if {@code androidx.appsearch.compiler.AppSearchCompiler} 963 * has not generated a schema for the given document class. 964 */ 965 @CanIgnoreReturnValue 966 @SuppressLint("MissingGetterMatchingBuilder") setDocumentClassDisplayedBySystem(@onNull Class<?> documentClass, boolean displayed)967 public @NonNull Builder setDocumentClassDisplayedBySystem(@NonNull Class<?> documentClass, 968 boolean displayed) throws AppSearchException { 969 Preconditions.checkNotNull(documentClass); 970 resetIfBuilt(); 971 DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance(); 972 DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass); 973 return setSchemaTypeDisplayedBySystem(factory.getSchemaName(), displayed); 974 } 975 976 /** 977 * Sets whether or not documents from the provided 978 * {@link androidx.appsearch.annotation.Document} annotated class can be read by the 979 * specified package. 980 * 981 * <p>Each package is represented by a {@link PackageIdentifier}, containing a package name 982 * and a byte array of type {@link android.content.pm.PackageManager#CERT_INPUT_SHA256}. 983 * 984 * <p>To opt into one-way data sharing with another application, the developer will need to 985 * explicitly grant the other application’s package name and certificate Read access to its 986 * data. 987 * 988 * <p>For two-way data sharing, both applications need to explicitly grant Read access to 989 * one another. 990 * 991 * <p>By default, app data sharing between applications is disabled. 992 * 993 * <p> The relationship between visible packages added in this method and permission 994 * visibility setting {@link #addRequiredPermissionsForSchemaTypeVisibility} is "OR". The 995 * caller could access the schema if they match ANY requirements. If you want to set 996 * "AND" requirements like a caller must hold required permissions AND it is a specified 997 * package, please use {@link #addSchemaTypeVisibleToConfig}. 998 * 999 * <p>Merged list available from {@link #getSchemasVisibleToPackages()}. 1000 * 1001 * @param documentClass The {@link androidx.appsearch.annotation.Document} class to set 1002 * visibility on. 1003 * @param visible Whether the {@code documentClass} will be visible or not. 1004 * @param packageIdentifier Represents the package that will be granted visibility. 1005 * @throws AppSearchException if {@code androidx.appsearch.compiler.AppSearchCompiler} 1006 * has not generated a schema for the given document class. 1007 */ 1008 @CanIgnoreReturnValue 1009 @SuppressLint("MissingGetterMatchingBuilder") setDocumentClassVisibilityForPackage( @onNull Class<?> documentClass, boolean visible, @NonNull PackageIdentifier packageIdentifier)1010 public @NonNull Builder setDocumentClassVisibilityForPackage( 1011 @NonNull Class<?> documentClass, boolean visible, 1012 @NonNull PackageIdentifier packageIdentifier) throws AppSearchException { 1013 Preconditions.checkNotNull(documentClass); 1014 resetIfBuilt(); 1015 DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance(); 1016 DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass); 1017 return setSchemaTypeVisibilityForPackage(factory.getSchemaName(), visible, 1018 packageIdentifier); 1019 } 1020 1021 /** 1022 * Adds a set of required Android {@link android.Manifest.permission} combination to the 1023 * given schema type. 1024 * 1025 * <p> If the querier holds ALL of the required permissions in this combination, they will 1026 * have access to read {@link GenericDocument} objects of the given schema type. 1027 * 1028 * <p> You can call this method to add multiple permission combinations, and the querier 1029 * will have access if they holds ANY of the combinations. 1030 * 1031 * <p>The supported Permissions are {@link #READ_SMS}, {@link #READ_CALENDAR}, 1032 * {@link #READ_CONTACTS}, {@link #READ_EXTERNAL_STORAGE}, 1033 * {@link #READ_HOME_APP_SEARCH_DATA} and {@link #READ_ASSISTANT_APP_SEARCH_DATA}. 1034 * 1035 * <p> The relationship between visible packages added in this method and permission 1036 * visibility setting {@link #addRequiredPermissionsForSchemaTypeVisibility} is "OR". The 1037 * caller could access the schema if they match ANY requirements. If you want to set 1038 * "AND" requirements like a caller must hold required permissions AND it is a specified 1039 * package, please use {@link #addSchemaTypeVisibleToConfig}. 1040 * 1041 * <p>Merged map available from {@link #getRequiredPermissionsForSchemaTypeVisibility()}. 1042 * @see android.Manifest.permission#READ_SMS 1043 * @see android.Manifest.permission#READ_CALENDAR 1044 * @see android.Manifest.permission#READ_CONTACTS 1045 * @see android.Manifest.permission#READ_EXTERNAL_STORAGE 1046 * @see android.Manifest.permission#READ_HOME_APP_SEARCH_DATA 1047 * @see android.Manifest.permission#READ_ASSISTANT_APP_SEARCH_DATA 1048 * @param documentClass The {@link androidx.appsearch.annotation.Document} class to set 1049 * visibility on. 1050 * @param permissions A set of required Android permissions the caller need to hold 1051 * to access {@link GenericDocument} objects that under the given 1052 * schema. 1053 * @throws IllegalArgumentException – if input unsupported permission. 1054 */ 1055 @CanIgnoreReturnValue 1056 @SuppressLint("MissingGetterMatchingBuilder") 1057 @RequiresFeature( 1058 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1059 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) addRequiredPermissionsForDocumentClassVisibility( @onNull Class<?> documentClass, @AppSearchSupportedPermission @NonNull Set<Integer> permissions)1060 public @NonNull Builder addRequiredPermissionsForDocumentClassVisibility( 1061 @NonNull Class<?> documentClass, 1062 @AppSearchSupportedPermission @NonNull Set<Integer> permissions) 1063 throws AppSearchException { 1064 Preconditions.checkNotNull(documentClass); 1065 resetIfBuilt(); 1066 DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance(); 1067 DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass); 1068 return addRequiredPermissionsForSchemaTypeVisibility( 1069 factory.getSchemaName(), permissions); 1070 } 1071 1072 /** Clears all required permissions combinations for the given schema type. */ 1073 @CanIgnoreReturnValue 1074 @RequiresFeature( 1075 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1076 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) clearRequiredPermissionsForDocumentClassVisibility( @onNull Class<?> documentClass)1077 public @NonNull Builder clearRequiredPermissionsForDocumentClassVisibility( 1078 @NonNull Class<?> documentClass) 1079 throws AppSearchException { 1080 Preconditions.checkNotNull(documentClass); 1081 resetIfBuilt(); 1082 DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance(); 1083 DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass); 1084 return clearRequiredPermissionsForSchemaTypeVisibility(factory.getSchemaName()); 1085 } 1086 1087 /** 1088 * Sets the documents from the provided {@code schemaType} can be read by the caller if they 1089 * match the ALL visibility requirements set in {@link SchemaVisibilityConfig}. 1090 * 1091 * <p> The requirements in a {@link SchemaVisibilityConfig} is "AND" relationship. A 1092 * caller must match ALL requirements to access the schema. For example, a caller must hold 1093 * required permissions AND it is a specified package. 1094 * 1095 * <p> You can call this method repeatedly to add multiple {@link SchemaVisibilityConfig}s, 1096 * and the querier will have access if they match ANY of the {@link SchemaVisibilityConfig}. 1097 * 1098 * @param documentClass A class annotated with 1099 * {@link androidx.appsearch.annotation.Document}, the 1100 * visibility of which will be configured 1101 * @param schemaVisibilityConfig The {@link SchemaVisibilityConfig} holds all 1102 * requirements that a call must to match to access the 1103 * schema. 1104 */ 1105 // Merged list available from getSchemasVisibleToConfigs 1106 @SuppressLint("MissingGetterMatchingBuilder") 1107 @RequiresFeature( 1108 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1109 name = Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG) 1110 @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS) addDocumentClassVisibleToConfig( @onNull Class<?> documentClass, @NonNull SchemaVisibilityConfig schemaVisibilityConfig)1111 public @NonNull Builder addDocumentClassVisibleToConfig( 1112 @NonNull Class<?> documentClass, 1113 @NonNull SchemaVisibilityConfig schemaVisibilityConfig) 1114 throws AppSearchException { 1115 Preconditions.checkNotNull(documentClass); 1116 resetIfBuilt(); 1117 DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance(); 1118 DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass); 1119 return addSchemaTypeVisibleToConfig(factory.getSchemaName(), 1120 schemaVisibilityConfig); 1121 } 1122 1123 /** Clears all visible to {@link SchemaVisibilityConfig} for the given schema type. */ 1124 @RequiresFeature( 1125 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1126 name = Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG) 1127 @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS) clearDocumentClassVisibleToConfigs( @onNull Class<?> documentClass)1128 public @NonNull Builder clearDocumentClassVisibleToConfigs( 1129 @NonNull Class<?> documentClass) throws AppSearchException { 1130 Preconditions.checkNotNull(documentClass); 1131 resetIfBuilt(); 1132 DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance(); 1133 DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass); 1134 return clearSchemaTypeVisibleToConfigs(factory.getSchemaName()); 1135 } 1136 // @exportToFramework:endStrip() 1137 1138 /** 1139 * Sets whether or not to override the current schema in the {@link AppSearchSession} 1140 * database. 1141 * 1142 * <p>Call this method whenever backward incompatible changes need to be made by setting 1143 * {@code forceOverride} to {@code true}. As a result, during execution of the setSchema 1144 * operation, all documents that are incompatible with the new schema will be deleted and 1145 * the new schema will be saved and persisted. 1146 * 1147 * <p>By default, this is {@code false}. 1148 */ 1149 @CanIgnoreReturnValue setForceOverride(boolean forceOverride)1150 public @NonNull Builder setForceOverride(boolean forceOverride) { 1151 resetIfBuilt(); 1152 mForceOverride = forceOverride; 1153 return this; 1154 } 1155 1156 /** 1157 * Sets the version number of the overall {@link AppSearchSchema} in the database. 1158 * 1159 * <p>The {@link AppSearchSession} database can only ever hold documents for one version 1160 * at a time. 1161 * 1162 * <p>Setting a version number that is different from the version number currently stored 1163 * in AppSearch will result in AppSearch calling the {@link Migrator}s provided to 1164 * {@link AppSearchSession#setSchemaAsync} to migrate the documents already in AppSearch from 1165 * the previous version to the one set in this request. The version number can be 1166 * updated without any other changes to the set of schemas. 1167 * 1168 * <p>The version number can stay the same, increase, or decrease relative to the current 1169 * version number that is already stored in the {@link AppSearchSession} database. 1170 * 1171 * <p>The version of an empty database will always be 0. You cannot set version to the 1172 * {@link SetSchemaRequest}, if it doesn't contains any {@link AppSearchSchema}. 1173 * 1174 * @param version A positive integer representing the version of the entire set of 1175 * schemas represents the version of the whole schema in the 1176 * {@link AppSearchSession} database, default version is 1. 1177 * 1178 * @throws IllegalArgumentException if the version is negative. 1179 * 1180 * @see AppSearchSession#setSchemaAsync 1181 * @see Migrator 1182 * @see SetSchemaRequest.Builder#setMigrator 1183 */ 1184 @CanIgnoreReturnValue setVersion(@ntRangefrom = 1) int version)1185 public @NonNull Builder setVersion(@IntRange(from = 1) int version) { 1186 Preconditions.checkArgument(version >= 1, "Version must be a positive number."); 1187 resetIfBuilt(); 1188 mVersion = version; 1189 return this; 1190 } 1191 1192 /** 1193 * Builds a new {@link SetSchemaRequest} object. 1194 * 1195 * @throws IllegalArgumentException if schema types were referenced, but the 1196 * corresponding {@link AppSearchSchema} type was never 1197 * added. 1198 */ build()1199 public @NonNull SetSchemaRequest build() { 1200 // Verify that any schema types with display or visibility settings refer to a real 1201 // schema. 1202 // Create a copy because we're going to remove from the set for verification purposes. 1203 Set<String> referencedSchemas = new ArraySet<>(mSchemasNotDisplayedBySystem); 1204 referencedSchemas.addAll(mSchemasVisibleToPackages.keySet()); 1205 referencedSchemas.addAll(mSchemasVisibleToPermissions.keySet()); 1206 referencedSchemas.addAll(mPubliclyVisibleSchemas.keySet()); 1207 referencedSchemas.addAll(mSchemaVisibleToConfigs.keySet()); 1208 1209 for (AppSearchSchema schema : mSchemas) { 1210 referencedSchemas.remove(schema.getSchemaType()); 1211 } 1212 if (!referencedSchemas.isEmpty()) { 1213 // We still have schema types that weren't seen in our mSchemas set. This means 1214 // there wasn't a corresponding AppSearchSchema. 1215 throw new IllegalArgumentException( 1216 "Schema types " + referencedSchemas + " referenced, but were not added."); 1217 } 1218 if (mSchemas.isEmpty() && mVersion != DEFAULT_VERSION) { 1219 throw new IllegalArgumentException( 1220 "Cannot set version to the request if schema is empty."); 1221 } 1222 mBuilt = true; 1223 return new SetSchemaRequest( 1224 mSchemas, 1225 mSchemasNotDisplayedBySystem, 1226 mSchemasVisibleToPackages, 1227 mSchemasVisibleToPermissions, 1228 mPubliclyVisibleSchemas, 1229 mSchemaVisibleToConfigs, 1230 mMigrators, 1231 mForceOverride, 1232 mVersion); 1233 } 1234 resetIfBuilt()1235 private void resetIfBuilt() { 1236 if (mBuilt) { 1237 ArrayMap<String, Set<PackageIdentifier>> schemasVisibleToPackages = 1238 new ArrayMap<>(mSchemasVisibleToPackages.size()); 1239 for (Map.Entry<String, Set<PackageIdentifier>> entry 1240 : mSchemasVisibleToPackages.entrySet()) { 1241 schemasVisibleToPackages.put(entry.getKey(), new ArraySet<>(entry.getValue())); 1242 } 1243 mSchemasVisibleToPackages = schemasVisibleToPackages; 1244 1245 mPubliclyVisibleSchemas = new ArrayMap<>(mPubliclyVisibleSchemas); 1246 1247 mSchemasVisibleToPermissions = deepCopy(mSchemasVisibleToPermissions); 1248 1249 ArrayMap<String, Set<SchemaVisibilityConfig>> schemaVisibleToConfigs = 1250 new ArrayMap<>(mSchemaVisibleToConfigs.size()); 1251 for (Map.Entry<String, Set<SchemaVisibilityConfig>> entry : 1252 mSchemaVisibleToConfigs.entrySet()) { 1253 schemaVisibleToConfigs.put(entry.getKey(), new ArraySet<>(entry.getValue())); 1254 } 1255 mSchemaVisibleToConfigs = schemaVisibleToConfigs; 1256 1257 mSchemas = new ArraySet<>(mSchemas); 1258 mSchemasNotDisplayedBySystem = new ArraySet<>(mSchemasNotDisplayedBySystem); 1259 mMigrators = new ArrayMap<>(mMigrators); 1260 mBuilt = false; 1261 } 1262 } 1263 } 1264 deepCopy( @onNull Map<String, Set<Set<Integer>>> original)1265 private static ArrayMap<String, Set<Set<Integer>>> deepCopy( 1266 @NonNull Map<String, Set<Set<Integer>>> original) { 1267 ArrayMap<String, Set<Set<Integer>>> copy = new ArrayMap<>(original.size()); 1268 for (Map.Entry<String, Set<Set<Integer>>> entry : original.entrySet()) { 1269 Set<Set<Integer>> valueCopy = new ArraySet<>(); 1270 for (Set<Integer> innerValue : entry.getValue()) { 1271 valueCopy.add(new ArraySet<>(innerValue)); 1272 } 1273 copy.put(entry.getKey(), valueCopy); 1274 } 1275 return copy; 1276 } 1277 } 1278