/* * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app.appsearch; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.appsearch.annotation.CanIgnoreReturnValue; import android.util.ArrayMap; import android.util.ArraySet; import com.android.appsearch.flags.Flags; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.Set; /** * Encapsulates a request to update the schema of an {@link AppSearchSession} database. * *
The schema is composed of a collection of {@link AppSearchSchema} objects, each of which * defines a unique type of data. * *
The first call to SetSchemaRequest will set the provided schema and store it within the {@link * AppSearchSession} database. * *
Subsequent calls will compare the provided schema to the previously saved schema, to determine * how to treat existing documents. * *
The following types of schema modifications are always safe and are made without deleting any * existing documents: * *
The following types of schema changes are not backwards compatible: * *
Providing a schema with incompatible changes, will throw an {@link * android.app.appsearch.exceptions.AppSearchException}, with a message describing the * incompatibility. As a result, the previously set schema will remain unchanged. * *
Backward incompatible changes can be made by : * *
It’s inefficient to call this method repeatedly.
*/
@NonNull
public Map The querier could read the {@link GenericDocument} objects under the {@code schemaType} if
* they holds ALL required permissions of ANY of the individual value sets.
*
* For example, if the Map contains {@code {% verbatim %}{{permissionA, PermissionB},
* {PermissionC, PermissionD}, {PermissionE}}{% endverbatim %}}.
*
* It’s inefficient to call this method repeatedly.
*
* @return The map contains schema type and all combinations of required permission for querier
* to access it. The supported Permission are {@link SetSchemaRequest#READ_SMS}, {@link
* SetSchemaRequest#READ_CALENDAR}, {@link SetSchemaRequest#READ_CONTACTS}, {@link
* SetSchemaRequest#READ_EXTERNAL_STORAGE}, {@link
* SetSchemaRequest#READ_HOME_APP_SEARCH_DATA} and {@link
* SetSchemaRequest#READ_ASSISTANT_APP_SEARCH_DATA}.
*/
// TODO(b/237388235): add enterprise permissions to javadocs after they're unhidden
@NonNull
public Map It’s inefficient to call this method repeatedly.
*
* @see SetSchemaRequest.Builder#addSchemaTypeVisibleToConfig
*/
@FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
@NonNull
public Map A more efficient version of {@link #getSchemasVisibleToPackages}, but it returns a
* modifiable map. This is not meant to be unhidden and should only be used by internal classes.
*
* @hide
*/
@NonNull
public Map An {@link AppSearchSchema} object represents one type of structured data.
*
* Any documents of these types will be displayed on system UI surfaces by default.
*/
@CanIgnoreReturnValue
@NonNull
public Builder addSchemas(@NonNull AppSearchSchema... schemas) {
Objects.requireNonNull(schemas);
resetIfBuilt();
return addSchemas(Arrays.asList(schemas));
}
/**
* Adds a collection of {@link AppSearchSchema} objects to the schema.
*
* An {@link AppSearchSchema} object represents one type of structured data.
*/
@CanIgnoreReturnValue
@NonNull
public Builder addSchemas(@NonNull Collection This setting applies to the provided {@code schemaType} only, and does not persist
* across {@link AppSearchSession#setSchema} calls.
*
* The default behavior, if this method is not called, is to allow types to be displayed
* on system UI surfaces.
*
* @param schemaType The name of an {@link AppSearchSchema} within the same {@link
* SetSchemaRequest}, which will be configured.
* @param displayed Whether documents of this type will be displayed on system UI surfaces.
*/
// Merged list available from getSchemasNotDisplayedBySystem
@CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setSchemaTypeDisplayedBySystem(
@NonNull String schemaType, boolean displayed) {
Objects.requireNonNull(schemaType);
resetIfBuilt();
if (displayed) {
mSchemasNotDisplayedBySystem.remove(schemaType);
} else {
mSchemasNotDisplayedBySystem.add(schemaType);
}
return this;
}
/**
* Adds a set of required Android {@link android.Manifest.permission} combination to the
* given schema type.
*
* If the querier holds ALL of the required permissions in this combination, they will
* have access to read {@link GenericDocument} objects of the given schema type.
*
* You can call this method to add multiple permission combinations, and the querier will
* have access if they holds ANY of the combinations.
*
* The supported Permissions are {@link #READ_SMS}, {@link #READ_CALENDAR}, {@link
* #READ_CONTACTS}, {@link #READ_EXTERNAL_STORAGE}, {@link #READ_HOME_APP_SEARCH_DATA} and
* {@link #READ_ASSISTANT_APP_SEARCH_DATA}.
*
* The relationship between permissions added in this method and package visibility
* setting {@link #setSchemaTypeVisibilityForPackage} is "OR". The caller could access the
* schema if they match ANY requirements. If you want to set "AND" requirements like a
* caller must hold required permissions AND it is a specified package, please use {@link
* #addSchemaTypeVisibleToConfig}.
*
* @see android.Manifest.permission#READ_SMS
* @see android.Manifest.permission#READ_CALENDAR
* @see android.Manifest.permission#READ_CONTACTS
* @see android.Manifest.permission#READ_EXTERNAL_STORAGE
* @see android.Manifest.permission#READ_HOME_APP_SEARCH_DATA
* @see android.Manifest.permission#READ_ASSISTANT_APP_SEARCH_DATA
* @param schemaType The schema type to set visibility on.
* @param permissions A set of required Android permissions the caller need to hold to
* access {@link GenericDocument} objects that under the given schema.
* @throws IllegalArgumentException – if input unsupported permission.
*/
// TODO(b/237388235): add enterprise permissions to javadocs after they're unhidden
// Merged list available from getRequiredPermissionsForSchemaTypeVisibility
@CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder addRequiredPermissionsForSchemaTypeVisibility(
@NonNull String schemaType,
@AppSearchSupportedPermission @NonNull Set Each package is represented by a {@link PackageIdentifier}, containing a package name
* and a byte array of type {@link android.content.pm.PackageManager#CERT_INPUT_SHA256}.
*
* To opt into one-way data sharing with another application, the developer will need to
* explicitly grant the other application’s package name and certificate Read access to its
* data.
*
* For two-way data sharing, both applications need to explicitly grant Read access to
* one another.
*
* By default, data sharing between applications is disabled.
*
* The relationship between permissions added in this method and package visibility
* setting {@link #setSchemaTypeVisibilityForPackage} is "OR". The caller could access the
* schema if they match ANY requirements. If you want to set "AND" requirements like a
* caller must hold required permissions AND it is a specified package, please use {@link
* #addSchemaTypeVisibleToConfig}.
*
* @param schemaType The schema type to set visibility on.
* @param visible Whether the {@code schemaType} will be visible or not.
* @param packageIdentifier Represents the package that will be granted visibility.
*/
// Merged list available from getSchemasVisibleToPackages
@CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setSchemaTypeVisibilityForPackage(
@NonNull String schemaType,
boolean visible,
@NonNull PackageIdentifier packageIdentifier) {
Objects.requireNonNull(schemaType);
Objects.requireNonNull(packageIdentifier);
resetIfBuilt();
Set It is possible for the packageIdentifier parameter to be different from the package
* performing the indexing. This might happen in the case of an on-device indexer processing
* information about various packages. The visibility will be the same regardless of which
* package indexes the document, as the visibility is based on the packageIdentifier
* parameter.
*
* If this is called repeatedly with the same schema, the {@link PackageIdentifier} in
* the last call will be used as the "from" package for that schema.
*
* Calling this with packageIdentifier set to null is valid, and will remove public
* visibility for the schema.
*
* @param schema the schema to make publicly accessible.
* @param packageIdentifier if an app can see this package via
* PackageManager#canPackageQuery, it will be able to see the documents of type {@code
* schema}.
*/
// Merged list available from getPubliclyVisibleSchemas
@CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@FlaggedApi(Flags.FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA)
@NonNull
public Builder setPubliclyVisibleSchema(
@NonNull String schema, @Nullable PackageIdentifier packageIdentifier) {
Objects.requireNonNull(schema);
resetIfBuilt();
// If the package identifier is null or empty we clear public visibility
if (packageIdentifier == null || packageIdentifier.getPackageName().isEmpty()) {
mPubliclyVisibleSchemas.remove(schema);
return this;
}
mPubliclyVisibleSchemas.put(schema, packageIdentifier);
return this;
}
/**
* Sets the documents from the provided {@code schemaType} can be read by the caller if they
* match the ALL visibility requirements set in {@link SchemaVisibilityConfig}.
*
* The requirements in a {@link SchemaVisibilityConfig} is "AND" relationship. A caller
* must match ALL requirements to access the schema. For example, a caller must hold
* required permissions AND it is a specified package.
*
* You can call this method repeatedly to add multiple {@link SchemaVisibilityConfig}s,
* and the querier will have access if they match ANY of the {@link SchemaVisibilityConfig}.
*
* @param schemaType The schema type to set visibility on.
* @param schemaVisibilityConfig The {@link SchemaVisibilityConfig} holds all requirements
* that a call must to match to access the schema.
*/
// Merged list available from getSchemasVisibleToConfigs
@CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
@NonNull
public Builder addSchemaTypeVisibleToConfig(
@NonNull String schemaType,
@NonNull SchemaVisibilityConfig schemaVisibilityConfig) {
Objects.requireNonNull(schemaType);
Objects.requireNonNull(schemaVisibilityConfig);
resetIfBuilt();
Set The {@link Migrator} migrates all {@link GenericDocument}s under given schema type
* from the current version number stored in AppSearch to the final version set via {@link
* #setVersion}.
*
* A {@link Migrator} will be invoked if the current version number stored in AppSearch
* is different from the final version set via {@link #setVersion} and {@link
* Migrator#shouldMigrate} returns {@code true}.
*
* The target schema type of the output {@link GenericDocument} of {@link
* Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this {@link
* SetSchemaRequest}.
*
* @param schemaType The schema type to set migrator on.
* @param migrator The migrator translates a document from its current version to the final
* version set via {@link #setVersion}.
* @see SetSchemaRequest.Builder#setVersion
* @see SetSchemaRequest.Builder#addSchemas
* @see AppSearchSession#setSchema
*/
@CanIgnoreReturnValue
@NonNull
@SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects.
public Builder setMigrator(@NonNull String schemaType, @NonNull Migrator migrator) {
Objects.requireNonNull(schemaType);
Objects.requireNonNull(migrator);
resetIfBuilt();
mMigrators.put(schemaType, migrator);
return this;
}
/**
* Sets a Map of {@link Migrator}s.
*
* The key of the map is the schema type that the {@link Migrator} value applies to.
*
* The {@link Migrator} migrates all {@link GenericDocument}s under given schema type
* from the current version number stored in AppSearch to the final version set via {@link
* #setVersion}.
*
* A {@link Migrator} will be invoked if the current version number stored in AppSearch
* is different from the final version set via {@link #setVersion} and {@link
* Migrator#shouldMigrate} returns {@code true}.
*
* The target schema type of the output {@link GenericDocument} of {@link
* Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this {@link
* SetSchemaRequest}.
*
* @param migrators A {@link Map} of migrators that translate a document from its current
* version to the final version set via {@link #setVersion}. The key of the map is the
* schema type that the {@link Migrator} value applies to.
* @see SetSchemaRequest.Builder#setVersion
* @see SetSchemaRequest.Builder#addSchemas
* @see AppSearchSession#setSchema
*/
@CanIgnoreReturnValue
@NonNull
public Builder setMigrators(@NonNull Map Call this method whenever backward incompatible changes need to be made by setting
* {@code forceOverride} to {@code true}. As a result, during execution of the setSchema
* operation, all documents that are incompatible with the new schema will be deleted and
* the new schema will be saved and persisted.
*
* By default, this is {@code false}.
*/
@CanIgnoreReturnValue
@NonNull
public Builder setForceOverride(boolean forceOverride) {
resetIfBuilt();
mForceOverride = forceOverride;
return this;
}
/**
* Sets the version number of the overall {@link AppSearchSchema} in the database.
*
* The {@link AppSearchSession} database can only ever hold documents for one version at
* a time.
*
* Setting a version number that is different from the version number currently stored in
* AppSearch will result in AppSearch calling the {@link Migrator}s provided to {@link
* AppSearchSession#setSchema} to migrate the documents already in AppSearch from the
* previous version to the one set in this request. The version number can be updated
* without any other changes to the set of schemas.
*
* The version number can stay the same, increase, or decrease relative to the current
* version number that is already stored in the {@link AppSearchSession} database.
*
* The version of an empty database will always be 0. You cannot set version to the
* {@link SetSchemaRequest}, if it doesn't contains any {@link AppSearchSchema}.
*
* @param version A positive integer representing the version of the entire set of schemas
* represents the version of the whole schema in the {@link AppSearchSession} database,
* default version is 1.
* @throws IllegalArgumentException if the version is negative.
* @see AppSearchSession#setSchema
* @see Migrator
* @see SetSchemaRequest.Builder#setMigrator
*/
@CanIgnoreReturnValue
@NonNull
public Builder setVersion(@IntRange(from = 1) int version) {
Preconditions.checkArgument(version >= 1, "Version must be a positive number.");
resetIfBuilt();
mVersion = version;
return this;
}
/**
* Builds a new {@link SetSchemaRequest} object.
*
* @throws IllegalArgumentException if schema types were referenced, but the corresponding
* {@link AppSearchSchema} type was never added.
*/
@NonNull
public SetSchemaRequest build() {
// Verify that any schema types with display or visibility settings refer to a real
// schema.
// Create a copy because we're going to remove from the set for verification purposes.
Set
*
*
*