All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.android.app.appsearch.SetSchemaRequest Maven / Gradle / Ivy

/*
 * 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.IntRange;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.util.ArrayMap;
import android.util.ArraySet;

import com.android.internal.util.Preconditions;

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: * *

    *
  • Addition of new {@link AppSearchSchema} types *
  • Addition of new properties to an existing {@link AppSearchSchema} type *
  • Changing the cardinality of a property to be less restrictive *
* *

The following types of schema changes are not backwards compatible: * *

    *
  • Removal of an existing {@link AppSearchSchema} type *
  • Removal of a property from an existing {@link AppSearchSchema} type *
  • Changing the data type of an existing property *
  • Changing the cardinality of a property to be more restrictive *
* *

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 : * *

    *
  • setting {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. This * deletes all documents that are incompatible with the new schema. The new schema is then * saved and persisted to disk. *
  • Add a {@link Migrator} for each incompatible type and make no deletion. The migrator will * migrate documents from it's old schema version to the new version. Migrated types will be * set into both {@link SetSchemaResponse#getIncompatibleTypes()} and {@link * SetSchemaResponse#getMigratedTypes()}. See the migration section below. *
* * @see AppSearchSession#setSchema * @see Migrator */ public final class SetSchemaRequest { private final Set mSchemas; private final Set mSchemasNotDisplayedBySystem; private final Map> mSchemasVisibleToPackages; private final Map mMigrators; private final boolean mForceOverride; private final int mVersion; SetSchemaRequest( @NonNull Set schemas, @NonNull Set schemasNotDisplayedBySystem, @NonNull Map> schemasVisibleToPackages, @NonNull Map migrators, boolean forceOverride, int version) { mSchemas = Objects.requireNonNull(schemas); mSchemasNotDisplayedBySystem = Objects.requireNonNull(schemasNotDisplayedBySystem); mSchemasVisibleToPackages = Objects.requireNonNull(schemasVisibleToPackages); mMigrators = Objects.requireNonNull(migrators); mForceOverride = forceOverride; mVersion = version; } /** Returns the {@link AppSearchSchema} types that are part of this request. */ @NonNull public Set getSchemas() { return Collections.unmodifiableSet(mSchemas); } /** * Returns all the schema types that are opted out of being displayed and visible on any system * UI surface. */ @NonNull public Set getSchemasNotDisplayedBySystem() { return Collections.unmodifiableSet(mSchemasNotDisplayedBySystem); } /** * Returns a mapping of schema types to the set of packages that have access to that schema * type. * *

It’s inefficient to call this method repeatedly. */ @NonNull public Map> getSchemasVisibleToPackages() { Map> copy = new ArrayMap<>(); for (String key : mSchemasVisibleToPackages.keySet()) { copy.put(key, new ArraySet<>(mSchemasVisibleToPackages.get(key))); } return copy; } /** * Returns the map of {@link Migrator}, the key will be the schema type of the {@link Migrator} * associated with. */ @NonNull public Map getMigrators() { return Collections.unmodifiableMap(mMigrators); } /** * Returns a mapping of {@link AppSearchSchema} types to the set of packages that have access to * that schema type. * *

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> getSchemasVisibleToPackagesInternal() { return mSchemasVisibleToPackages; } /** Returns whether this request will force the schema to be overridden. */ public boolean isForceOverride() { return mForceOverride; } /** Returns the database overall schema version. */ @IntRange(from = 1) public int getVersion() { return mVersion; } /** Builder for {@link SetSchemaRequest} objects. */ public static final class Builder { private static final int DEFAULT_VERSION = 1; private ArraySet mSchemas = new ArraySet<>(); private ArraySet mSchemasNotDisplayedBySystem = new ArraySet<>(); private ArrayMap> mSchemasVisibleToPackages = new ArrayMap<>(); private ArrayMap mMigrators = new ArrayMap<>(); private boolean mForceOverride = false; private int mVersion = DEFAULT_VERSION; private boolean mBuilt = false; /** * Adds one or more {@link AppSearchSchema} types to the schema. * *

An {@link AppSearchSchema} object represents one type of structured data. * *

Any documents of these types will be displayed on system UI surfaces by default. */ @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. */ @NonNull public Builder addSchemas(@NonNull Collection schemas) { Objects.requireNonNull(schemas); resetIfBuilt(); mSchemas.addAll(schemas); return this; } /** * Sets whether or not documents from the provided {@code schemaType} will be displayed and * visible on any system UI surface. * *

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 @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; } /** * Sets whether or not documents from the provided {@code schemaType} can be read by the * specified package. * *

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. * * @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 @SuppressLint("MissingGetterMatchingBuilder") @NonNull public Builder setSchemaTypeVisibilityForPackage( @NonNull String schemaType, boolean visible, @NonNull PackageIdentifier packageIdentifier) { Objects.requireNonNull(schemaType); Objects.requireNonNull(packageIdentifier); resetIfBuilt(); Set packageIdentifiers = mSchemasVisibleToPackages.get(schemaType); if (visible) { if (packageIdentifiers == null) { packageIdentifiers = new ArraySet<>(); } packageIdentifiers.add(packageIdentifier); mSchemasVisibleToPackages.put(schemaType, packageIdentifiers); } else { if (packageIdentifiers == null) { // Return early since there was nothing set to begin with. return this; } packageIdentifiers.remove(packageIdentifier); if (packageIdentifiers.isEmpty()) { // Remove the entire key so that we don't have empty sets as values. mSchemasVisibleToPackages.remove(schemaType); } } return this; } /** * Sets the {@link Migrator} associated with the given SchemaType. * *

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 */ @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 it's 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 */ @NonNull public Builder setMigrators(@NonNull Map migrators) { Objects.requireNonNull(migrators); resetIfBuilt(); mMigrators.putAll(migrators); return this; } /** * Sets whether or not to override the current schema in the {@link AppSearchSession} * database. * *

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}. */ @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 */ @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 referencedSchemas = new ArraySet<>(mSchemasNotDisplayedBySystem); referencedSchemas.addAll(mSchemasVisibleToPackages.keySet()); for (AppSearchSchema schema : mSchemas) { referencedSchemas.remove(schema.getSchemaType()); } if (!referencedSchemas.isEmpty()) { // We still have schema types that weren't seen in our mSchemas set. This means // there wasn't a corresponding AppSearchSchema. throw new IllegalArgumentException( "Schema types " + referencedSchemas + " referenced, but were not added."); } if (mSchemas.isEmpty() && mVersion != DEFAULT_VERSION) { throw new IllegalArgumentException( "Cannot set version to the request if schema is empty."); } mBuilt = true; return new SetSchemaRequest( mSchemas, mSchemasNotDisplayedBySystem, mSchemasVisibleToPackages, mMigrators, mForceOverride, mVersion); } private void resetIfBuilt() { if (mBuilt) { ArrayMap> schemasVisibleToPackages = new ArrayMap<>(mSchemasVisibleToPackages.size()); for (Map.Entry> entry : mSchemasVisibleToPackages.entrySet()) { schemasVisibleToPackages.put(entry.getKey(), new ArraySet<>(entry.getValue())); } mSchemasVisibleToPackages = schemasVisibleToPackages; mSchemas = new ArraySet<>(mSchemas); mSchemasNotDisplayedBySystem = new ArraySet<>(mSchemasNotDisplayedBySystem); mMigrators = new ArrayMap<>(mMigrators); mBuilt = false; } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy