Loading apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +6 −10 Original line number Diff line number Diff line Loading @@ -18,12 +18,12 @@ package android.app.appsearch; import android.annotation.NonNull; import android.annotation.SystemService; import android.content.Context; import android.os.Bundle; import android.os.RemoteException; import com.android.internal.infra.AndroidFuture; import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.SchemaProto; import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SearchSpecProto; import com.google.android.icing.proto.StatusProto; Loading Loading @@ -133,19 +133,15 @@ public class AppSearchManager { @NonNull public AppSearchResult<Void> setSchema( @NonNull List<AppSearchSchema> schemas, boolean forceOverride) { // Prepare the merged schema for transmission. SchemaProto.Builder schemaProtoBuilder = SchemaProto.newBuilder(); for (AppSearchSchema schema : schemas) { schemaProtoBuilder.addTypes(schema.getProto()); } // Serialize and send the schema. // TODO: This should use com.android.internal.infra.RemoteStream or another mechanism to // avoid binder limits. byte[] schemaBytes = schemaProtoBuilder.build().toByteArray(); List<Bundle> schemaBundles = new ArrayList<>(schemas.size()); for (AppSearchSchema schema : schemas) { schemaBundles.add(schema.getBundle()); } AndroidFuture<AppSearchResult> future = new AndroidFuture<>(); try { mService.setSchema(schemaBytes, forceOverride, future); mService.setSchema(schemaBundles, forceOverride, future); } catch (RemoteException e) { future.completeExceptionally(e); } Loading apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java +127 −93 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * 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. Loading @@ -16,18 +16,18 @@ package android.app.appsearch; import android.os.Bundle; import android.annotation.IntDef; import android.annotation.NonNull; import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.google.android.icing.proto.PropertyConfigProto; import com.google.android.icing.proto.SchemaTypeConfigProto; import com.google.android.icing.proto.TermMatchType; import android.app.appsearch.exceptions.IllegalSchemaException; import android.util.ArraySet; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Set; /** Loading @@ -36,45 +36,64 @@ import java.util.Set; * <p>For example, an e-mail message or a music recording could be a schema type. * * <p>The schema consists of type information, properties, and config (like tokenization type). * * @hide */ public final class AppSearchSchema { private final SchemaTypeConfigProto mProto; /** @hide */ public static final String SCHEMA_TYPE_FIELD = "schemaType"; private AppSearchSchema(SchemaTypeConfigProto proto) { mProto = proto; /** @hide */ public static final String PROPERTIES_FIELD = "properties"; private final Bundle mBundle; /** @hide */ public AppSearchSchema(@NonNull Bundle bundle) { Preconditions.checkNotNull(bundle); mBundle = bundle; } /** * Returns the {@link SchemaTypeConfigProto} populated by this builder. * Returns the {@link Bundle} populated by this builder. * @hide */ @NonNull @VisibleForTesting public SchemaTypeConfigProto getProto() { return mProto; public Bundle getBundle() { return mBundle; } @Override public String toString() { return mProto.toString(); return mBundle.toString(); } /** Builder for {@link AppSearchSchema objects}. */ public static final class Builder { private final SchemaTypeConfigProto.Builder mProtoBuilder = SchemaTypeConfigProto.newBuilder(); private final String mTypeName; private final ArrayList<Bundle> mProperties = new ArrayList<>(); private final Set<String> mPropertyNames = new ArraySet<>(); private boolean mBuilt = false; /** Creates a new {@link AppSearchSchema.Builder}. */ public Builder(@NonNull String typeName) { mProtoBuilder.setSchemaType(typeName); Preconditions.checkNotNull(typeName); mTypeName = typeName; } /** Adds a property to the given type. */ @NonNull public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) { mProtoBuilder.addProperties(propertyConfig.mProto); Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(propertyConfig); if (!mPropertyNames.add(propertyConfig.mName)) { throw new IllegalSchemaException( "Property defined more than once: " + propertyConfig.mName); } mProperties.add(propertyConfig.mBundle); return this; } Loading @@ -85,15 +104,12 @@ public final class AppSearchSchema { */ @NonNull public AppSearchSchema build() { Set<String> propertyNames = new ArraySet<>(); for (PropertyConfigProto propertyConfigProto : mProtoBuilder.getPropertiesList()) { if (!propertyNames.add(propertyConfigProto.getPropertyName())) { throw new IllegalSchemaException( "Property defined more than once: " + propertyConfigProto.getPropertyName()); } } return new AppSearchSchema(mProtoBuilder.build()); Preconditions.checkState(!mBuilt, "Builder has already been used"); Bundle bundle = new Bundle(); bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mTypeName); bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mProperties); mBuilt = true; return new AppSearchSchema(bundle); } } Loading @@ -104,10 +120,37 @@ public final class AppSearchSchema { * a property. */ public static final class PropertyConfig { /** Physical data-types of the contents of the property. */ /** @hide */ public static final String NAME_FIELD = "name"; /** @hide */ public static final String DATA_TYPE_FIELD = "dataType"; /** @hide */ public static final String SCHEMA_TYPE_FIELD = "schemaType"; /** @hide */ public static final String CARDINALITY_FIELD = "cardinality"; /** @hide */ public static final String INDEXING_TYPE_FIELD = "indexingType"; /** @hide */ public static final String TOKENIZER_TYPE_FIELD = "tokenizerType"; /** * Physical data-types of the contents of the property. * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in // com.google.android.icing.proto.PropertyConfigProto.DataType.Code. @IntDef(prefix = {"DATA_TYPE_"}, value = { @IntDef(value = { DATA_TYPE_STRING, DATA_TYPE_INT64, DATA_TYPE_DOUBLE, Loading @@ -133,10 +176,13 @@ public final class AppSearchSchema { */ public static final int DATA_TYPE_DOCUMENT = 6; /** The cardinality of the property (whether it is required, optional or repeated). */ /** * The cardinality of the property (whether it is required, optional or repeated). * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in // com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code. @IntDef(prefix = {"CARDINALITY_"}, value = { @IntDef(value = { CARDINALITY_REPEATED, CARDINALITY_OPTIONAL, CARDINALITY_REQUIRED, Loading @@ -153,8 +199,11 @@ public final class AppSearchSchema { /** Exactly one value [1]. */ public static final int CARDINALITY_REQUIRED = 3; /** Encapsulates the configurations on how AppSearch should query/index these terms. */ @IntDef(prefix = {"INDEXING_TYPE_"}, value = { /** * Encapsulates the configurations on how AppSearch should query/index these terms. * @hide */ @IntDef(value = { INDEXING_TYPE_NONE, INDEXING_TYPE_EXACT_TERMS, INDEXING_TYPE_PREFIXES, Loading Loading @@ -188,10 +237,13 @@ public final class AppSearchSchema { */ public static final int INDEXING_TYPE_PREFIXES = 2; /** Configures how tokens should be extracted from this property. */ /** * Configures how tokens should be extracted from this property. * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in // com.google.android.icing.proto.IndexingConfig.TokenizerType.Code. @IntDef(prefix = {"TOKENIZER_TYPE_"}, value = { @IntDef(value = { TOKENIZER_TYPE_NONE, TOKENIZER_TYPE_PLAIN, }) Loading @@ -207,15 +259,17 @@ public final class AppSearchSchema { /** Tokenization for plain text. */ public static final int TOKENIZER_TYPE_PLAIN = 1; private final PropertyConfigProto mProto; final String mName; final Bundle mBundle; private PropertyConfig(PropertyConfigProto proto) { mProto = proto; PropertyConfig(@NonNull String name, @NonNull Bundle bundle) { mName = Preconditions.checkNotNull(name); mBundle = Preconditions.checkNotNull(bundle); } @Override public String toString() { return mProto.toString(); return mBundle.toString(); } /** Loading @@ -232,15 +286,14 @@ public final class AppSearchSchema { * is also required. */ public static final class Builder { private final PropertyConfigProto.Builder mPropertyConfigProto = PropertyConfigProto.newBuilder(); private final com.google.android.icing.proto.IndexingConfig.Builder mIndexingConfigProto = com.google.android.icing.proto.IndexingConfig.newBuilder(); private final String mName; private final Bundle mBundle = new Bundle(); private boolean mBuilt = false; /** Creates a new {@link PropertyConfig.Builder}. */ public Builder(@NonNull String propertyName) { mPropertyConfigProto.setPropertyName(propertyName); mName = Preconditions.checkNotNull(propertyName); mBundle.putString(NAME_FIELD, propertyName); } /** Loading @@ -250,24 +303,24 @@ public final class AppSearchSchema { */ @NonNull public PropertyConfig.Builder setDataType(@DataType int dataType) { PropertyConfigProto.DataType.Code dataTypeProto = PropertyConfigProto.DataType.Code.forNumber(dataType); if (dataTypeProto == null) { throw new IllegalArgumentException("Invalid dataType: " + dataType); } mPropertyConfigProto.setDataType(dataTypeProto); Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange( dataType, DATA_TYPE_STRING, DATA_TYPE_DOCUMENT, "dataType"); mBundle.putInt(DATA_TYPE_FIELD, dataType); return this; } /** * The logical schema-type of the contents of this property. * * <p>Only required when {@link #setDataType(int)} is set to * <p>Only required when {@link #setDataType} is set to * {@link #DATA_TYPE_DOCUMENT}. Otherwise, it is ignored. */ @NonNull public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) { mPropertyConfigProto.setSchemaType(schemaType); Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(schemaType); mBundle.putString(SCHEMA_TYPE_FIELD, schemaType); return this; } Loading @@ -278,12 +331,10 @@ public final class AppSearchSchema { */ @NonNull public PropertyConfig.Builder setCardinality(@Cardinality int cardinality) { PropertyConfigProto.Cardinality.Code cardinalityProto = PropertyConfigProto.Cardinality.Code.forNumber(cardinality); if (cardinalityProto == null) { throw new IllegalArgumentException("Invalid cardinality: " + cardinality); } mPropertyConfigProto.setCardinality(cardinalityProto); Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange( cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); mBundle.putInt(CARDINALITY_FIELD, cardinality); return this; } Loading @@ -292,35 +343,20 @@ public final class AppSearchSchema { */ @NonNull public PropertyConfig.Builder setIndexingType(@IndexingType int indexingType) { TermMatchType.Code termMatchTypeProto; switch (indexingType) { case INDEXING_TYPE_NONE: termMatchTypeProto = TermMatchType.Code.UNKNOWN; break; case INDEXING_TYPE_EXACT_TERMS: termMatchTypeProto = TermMatchType.Code.EXACT_ONLY; break; case INDEXING_TYPE_PREFIXES: termMatchTypeProto = TermMatchType.Code.PREFIX; break; default: throw new IllegalArgumentException("Invalid indexingType: " + indexingType); } mIndexingConfigProto.setTermMatchType(termMatchTypeProto); Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange( indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_PREFIXES, "indexingType"); mBundle.putInt(INDEXING_TYPE_FIELD, indexingType); return this; } /** Configures how this property should be tokenized (split into words). */ @NonNull public PropertyConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) { com.google.android.icing.proto.IndexingConfig.TokenizerType.Code tokenizerTypeProto = com.google.android.icing.proto.IndexingConfig .TokenizerType.Code.forNumber(tokenizerType); if (tokenizerTypeProto == null) { throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType); } mIndexingConfigProto.setTokenizerType(tokenizerTypeProto); Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange( tokenizerType, TOKENIZER_TYPE_NONE, TOKENIZER_TYPE_PLAIN, "tokenizerType"); mBundle.putInt(TOKENIZER_TYPE_FIELD, tokenizerType); return this; } Loading @@ -334,25 +370,23 @@ public final class AppSearchSchema { */ @NonNull public PropertyConfig build() { mPropertyConfigProto.setIndexingConfig(mIndexingConfigProto); Preconditions.checkState(!mBuilt, "Builder has already been used"); // TODO(b/147692920): Send the schema to Icing Lib for official validation, instead // of partially reimplementing some of the validation Icing does here. if (mPropertyConfigProto.getDataType() == PropertyConfigProto.DataType.Code.UNKNOWN) { if (!mBundle.containsKey(DATA_TYPE_FIELD)) { throw new IllegalSchemaException("Missing field: dataType"); } if (mPropertyConfigProto.getSchemaType().isEmpty() && mPropertyConfigProto.getDataType() == PropertyConfigProto.DataType.Code.DOCUMENT) { if (mBundle.getString(SCHEMA_TYPE_FIELD, "").isEmpty() && mBundle.getInt(DATA_TYPE_FIELD) == DATA_TYPE_DOCUMENT) { throw new IllegalSchemaException( "Missing field: schemaType (required for configs with " + "dataType = DOCUMENT)"); } if (mPropertyConfigProto.getCardinality() == PropertyConfigProto.Cardinality.Code.UNKNOWN) { if (!mBundle.containsKey(CARDINALITY_FIELD)) { throw new IllegalSchemaException("Missing field: cardinality"); } return new PropertyConfig(mPropertyConfigProto.build()); mBuilt = true; return new PropertyConfig(mName, mBundle); } } } Loading apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +6 −2 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ */ package android.app.appsearch; import android.os.Bundle; import com.android.internal.infra.AndroidFuture; parcelable AppSearchResult; Loading @@ -25,14 +27,16 @@ interface IAppSearchManager { /** * Sets the schema. * * @param schemaBytes Serialized SchemaProto. * @param schemaBundles List of AppSearchSchema bundles. * @param forceOverride Whether to apply the new schema even if it is incompatible. All * incompatible documents will be deleted. * @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link Void}>>. * The results of the call. */ void setSchema( in byte[] schemaBytes, boolean forceOverride, in AndroidFuture<AppSearchResult> callback); in List<Bundle> schemaBundles, boolean forceOverride, in AndroidFuture<AppSearchResult> callback); /** * Inserts documents into the index. Loading apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java +1 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.app.appsearch; import android.annotation.IntDef; import android.annotation.NonNull; import android.app.appsearch.exceptions.IllegalSearchSpecException; import com.google.android.icing.proto.ResultSpecProto; import com.google.android.icing.proto.ScoringSpecProto; Loading apex/appsearch/framework/java/android/app/appsearch/IllegalSchemaException.java→apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java +4 −2 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * 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. Loading @@ -14,16 +14,18 @@ * limitations under the License. */ package android.app.appsearch; package android.app.appsearch.exceptions; import android.annotation.NonNull; /** * Indicates that a {@link android.app.appsearch.AppSearchSchema} has logical inconsistencies such * as unpopulated mandatory fields or illegal combinations of parameters. * * @hide */ public class IllegalSchemaException extends IllegalArgumentException { /** * Constructs a new {@link IllegalSchemaException}. Loading Loading
apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +6 −10 Original line number Diff line number Diff line Loading @@ -18,12 +18,12 @@ package android.app.appsearch; import android.annotation.NonNull; import android.annotation.SystemService; import android.content.Context; import android.os.Bundle; import android.os.RemoteException; import com.android.internal.infra.AndroidFuture; import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.SchemaProto; import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SearchSpecProto; import com.google.android.icing.proto.StatusProto; Loading Loading @@ -133,19 +133,15 @@ public class AppSearchManager { @NonNull public AppSearchResult<Void> setSchema( @NonNull List<AppSearchSchema> schemas, boolean forceOverride) { // Prepare the merged schema for transmission. SchemaProto.Builder schemaProtoBuilder = SchemaProto.newBuilder(); for (AppSearchSchema schema : schemas) { schemaProtoBuilder.addTypes(schema.getProto()); } // Serialize and send the schema. // TODO: This should use com.android.internal.infra.RemoteStream or another mechanism to // avoid binder limits. byte[] schemaBytes = schemaProtoBuilder.build().toByteArray(); List<Bundle> schemaBundles = new ArrayList<>(schemas.size()); for (AppSearchSchema schema : schemas) { schemaBundles.add(schema.getBundle()); } AndroidFuture<AppSearchResult> future = new AndroidFuture<>(); try { mService.setSchema(schemaBytes, forceOverride, future); mService.setSchema(schemaBundles, forceOverride, future); } catch (RemoteException e) { future.completeExceptionally(e); } Loading
apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java +127 −93 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * 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. Loading @@ -16,18 +16,18 @@ package android.app.appsearch; import android.os.Bundle; import android.annotation.IntDef; import android.annotation.NonNull; import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.google.android.icing.proto.PropertyConfigProto; import com.google.android.icing.proto.SchemaTypeConfigProto; import com.google.android.icing.proto.TermMatchType; import android.app.appsearch.exceptions.IllegalSchemaException; import android.util.ArraySet; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Set; /** Loading @@ -36,45 +36,64 @@ import java.util.Set; * <p>For example, an e-mail message or a music recording could be a schema type. * * <p>The schema consists of type information, properties, and config (like tokenization type). * * @hide */ public final class AppSearchSchema { private final SchemaTypeConfigProto mProto; /** @hide */ public static final String SCHEMA_TYPE_FIELD = "schemaType"; private AppSearchSchema(SchemaTypeConfigProto proto) { mProto = proto; /** @hide */ public static final String PROPERTIES_FIELD = "properties"; private final Bundle mBundle; /** @hide */ public AppSearchSchema(@NonNull Bundle bundle) { Preconditions.checkNotNull(bundle); mBundle = bundle; } /** * Returns the {@link SchemaTypeConfigProto} populated by this builder. * Returns the {@link Bundle} populated by this builder. * @hide */ @NonNull @VisibleForTesting public SchemaTypeConfigProto getProto() { return mProto; public Bundle getBundle() { return mBundle; } @Override public String toString() { return mProto.toString(); return mBundle.toString(); } /** Builder for {@link AppSearchSchema objects}. */ public static final class Builder { private final SchemaTypeConfigProto.Builder mProtoBuilder = SchemaTypeConfigProto.newBuilder(); private final String mTypeName; private final ArrayList<Bundle> mProperties = new ArrayList<>(); private final Set<String> mPropertyNames = new ArraySet<>(); private boolean mBuilt = false; /** Creates a new {@link AppSearchSchema.Builder}. */ public Builder(@NonNull String typeName) { mProtoBuilder.setSchemaType(typeName); Preconditions.checkNotNull(typeName); mTypeName = typeName; } /** Adds a property to the given type. */ @NonNull public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) { mProtoBuilder.addProperties(propertyConfig.mProto); Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(propertyConfig); if (!mPropertyNames.add(propertyConfig.mName)) { throw new IllegalSchemaException( "Property defined more than once: " + propertyConfig.mName); } mProperties.add(propertyConfig.mBundle); return this; } Loading @@ -85,15 +104,12 @@ public final class AppSearchSchema { */ @NonNull public AppSearchSchema build() { Set<String> propertyNames = new ArraySet<>(); for (PropertyConfigProto propertyConfigProto : mProtoBuilder.getPropertiesList()) { if (!propertyNames.add(propertyConfigProto.getPropertyName())) { throw new IllegalSchemaException( "Property defined more than once: " + propertyConfigProto.getPropertyName()); } } return new AppSearchSchema(mProtoBuilder.build()); Preconditions.checkState(!mBuilt, "Builder has already been used"); Bundle bundle = new Bundle(); bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mTypeName); bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mProperties); mBuilt = true; return new AppSearchSchema(bundle); } } Loading @@ -104,10 +120,37 @@ public final class AppSearchSchema { * a property. */ public static final class PropertyConfig { /** Physical data-types of the contents of the property. */ /** @hide */ public static final String NAME_FIELD = "name"; /** @hide */ public static final String DATA_TYPE_FIELD = "dataType"; /** @hide */ public static final String SCHEMA_TYPE_FIELD = "schemaType"; /** @hide */ public static final String CARDINALITY_FIELD = "cardinality"; /** @hide */ public static final String INDEXING_TYPE_FIELD = "indexingType"; /** @hide */ public static final String TOKENIZER_TYPE_FIELD = "tokenizerType"; /** * Physical data-types of the contents of the property. * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in // com.google.android.icing.proto.PropertyConfigProto.DataType.Code. @IntDef(prefix = {"DATA_TYPE_"}, value = { @IntDef(value = { DATA_TYPE_STRING, DATA_TYPE_INT64, DATA_TYPE_DOUBLE, Loading @@ -133,10 +176,13 @@ public final class AppSearchSchema { */ public static final int DATA_TYPE_DOCUMENT = 6; /** The cardinality of the property (whether it is required, optional or repeated). */ /** * The cardinality of the property (whether it is required, optional or repeated). * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in // com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code. @IntDef(prefix = {"CARDINALITY_"}, value = { @IntDef(value = { CARDINALITY_REPEATED, CARDINALITY_OPTIONAL, CARDINALITY_REQUIRED, Loading @@ -153,8 +199,11 @@ public final class AppSearchSchema { /** Exactly one value [1]. */ public static final int CARDINALITY_REQUIRED = 3; /** Encapsulates the configurations on how AppSearch should query/index these terms. */ @IntDef(prefix = {"INDEXING_TYPE_"}, value = { /** * Encapsulates the configurations on how AppSearch should query/index these terms. * @hide */ @IntDef(value = { INDEXING_TYPE_NONE, INDEXING_TYPE_EXACT_TERMS, INDEXING_TYPE_PREFIXES, Loading Loading @@ -188,10 +237,13 @@ public final class AppSearchSchema { */ public static final int INDEXING_TYPE_PREFIXES = 2; /** Configures how tokens should be extracted from this property. */ /** * Configures how tokens should be extracted from this property. * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in // com.google.android.icing.proto.IndexingConfig.TokenizerType.Code. @IntDef(prefix = {"TOKENIZER_TYPE_"}, value = { @IntDef(value = { TOKENIZER_TYPE_NONE, TOKENIZER_TYPE_PLAIN, }) Loading @@ -207,15 +259,17 @@ public final class AppSearchSchema { /** Tokenization for plain text. */ public static final int TOKENIZER_TYPE_PLAIN = 1; private final PropertyConfigProto mProto; final String mName; final Bundle mBundle; private PropertyConfig(PropertyConfigProto proto) { mProto = proto; PropertyConfig(@NonNull String name, @NonNull Bundle bundle) { mName = Preconditions.checkNotNull(name); mBundle = Preconditions.checkNotNull(bundle); } @Override public String toString() { return mProto.toString(); return mBundle.toString(); } /** Loading @@ -232,15 +286,14 @@ public final class AppSearchSchema { * is also required. */ public static final class Builder { private final PropertyConfigProto.Builder mPropertyConfigProto = PropertyConfigProto.newBuilder(); private final com.google.android.icing.proto.IndexingConfig.Builder mIndexingConfigProto = com.google.android.icing.proto.IndexingConfig.newBuilder(); private final String mName; private final Bundle mBundle = new Bundle(); private boolean mBuilt = false; /** Creates a new {@link PropertyConfig.Builder}. */ public Builder(@NonNull String propertyName) { mPropertyConfigProto.setPropertyName(propertyName); mName = Preconditions.checkNotNull(propertyName); mBundle.putString(NAME_FIELD, propertyName); } /** Loading @@ -250,24 +303,24 @@ public final class AppSearchSchema { */ @NonNull public PropertyConfig.Builder setDataType(@DataType int dataType) { PropertyConfigProto.DataType.Code dataTypeProto = PropertyConfigProto.DataType.Code.forNumber(dataType); if (dataTypeProto == null) { throw new IllegalArgumentException("Invalid dataType: " + dataType); } mPropertyConfigProto.setDataType(dataTypeProto); Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange( dataType, DATA_TYPE_STRING, DATA_TYPE_DOCUMENT, "dataType"); mBundle.putInt(DATA_TYPE_FIELD, dataType); return this; } /** * The logical schema-type of the contents of this property. * * <p>Only required when {@link #setDataType(int)} is set to * <p>Only required when {@link #setDataType} is set to * {@link #DATA_TYPE_DOCUMENT}. Otherwise, it is ignored. */ @NonNull public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) { mPropertyConfigProto.setSchemaType(schemaType); Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(schemaType); mBundle.putString(SCHEMA_TYPE_FIELD, schemaType); return this; } Loading @@ -278,12 +331,10 @@ public final class AppSearchSchema { */ @NonNull public PropertyConfig.Builder setCardinality(@Cardinality int cardinality) { PropertyConfigProto.Cardinality.Code cardinalityProto = PropertyConfigProto.Cardinality.Code.forNumber(cardinality); if (cardinalityProto == null) { throw new IllegalArgumentException("Invalid cardinality: " + cardinality); } mPropertyConfigProto.setCardinality(cardinalityProto); Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange( cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); mBundle.putInt(CARDINALITY_FIELD, cardinality); return this; } Loading @@ -292,35 +343,20 @@ public final class AppSearchSchema { */ @NonNull public PropertyConfig.Builder setIndexingType(@IndexingType int indexingType) { TermMatchType.Code termMatchTypeProto; switch (indexingType) { case INDEXING_TYPE_NONE: termMatchTypeProto = TermMatchType.Code.UNKNOWN; break; case INDEXING_TYPE_EXACT_TERMS: termMatchTypeProto = TermMatchType.Code.EXACT_ONLY; break; case INDEXING_TYPE_PREFIXES: termMatchTypeProto = TermMatchType.Code.PREFIX; break; default: throw new IllegalArgumentException("Invalid indexingType: " + indexingType); } mIndexingConfigProto.setTermMatchType(termMatchTypeProto); Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange( indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_PREFIXES, "indexingType"); mBundle.putInt(INDEXING_TYPE_FIELD, indexingType); return this; } /** Configures how this property should be tokenized (split into words). */ @NonNull public PropertyConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) { com.google.android.icing.proto.IndexingConfig.TokenizerType.Code tokenizerTypeProto = com.google.android.icing.proto.IndexingConfig .TokenizerType.Code.forNumber(tokenizerType); if (tokenizerTypeProto == null) { throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType); } mIndexingConfigProto.setTokenizerType(tokenizerTypeProto); Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange( tokenizerType, TOKENIZER_TYPE_NONE, TOKENIZER_TYPE_PLAIN, "tokenizerType"); mBundle.putInt(TOKENIZER_TYPE_FIELD, tokenizerType); return this; } Loading @@ -334,25 +370,23 @@ public final class AppSearchSchema { */ @NonNull public PropertyConfig build() { mPropertyConfigProto.setIndexingConfig(mIndexingConfigProto); Preconditions.checkState(!mBuilt, "Builder has already been used"); // TODO(b/147692920): Send the schema to Icing Lib for official validation, instead // of partially reimplementing some of the validation Icing does here. if (mPropertyConfigProto.getDataType() == PropertyConfigProto.DataType.Code.UNKNOWN) { if (!mBundle.containsKey(DATA_TYPE_FIELD)) { throw new IllegalSchemaException("Missing field: dataType"); } if (mPropertyConfigProto.getSchemaType().isEmpty() && mPropertyConfigProto.getDataType() == PropertyConfigProto.DataType.Code.DOCUMENT) { if (mBundle.getString(SCHEMA_TYPE_FIELD, "").isEmpty() && mBundle.getInt(DATA_TYPE_FIELD) == DATA_TYPE_DOCUMENT) { throw new IllegalSchemaException( "Missing field: schemaType (required for configs with " + "dataType = DOCUMENT)"); } if (mPropertyConfigProto.getCardinality() == PropertyConfigProto.Cardinality.Code.UNKNOWN) { if (!mBundle.containsKey(CARDINALITY_FIELD)) { throw new IllegalSchemaException("Missing field: cardinality"); } return new PropertyConfig(mPropertyConfigProto.build()); mBuilt = true; return new PropertyConfig(mName, mBundle); } } } Loading
apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +6 −2 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ */ package android.app.appsearch; import android.os.Bundle; import com.android.internal.infra.AndroidFuture; parcelable AppSearchResult; Loading @@ -25,14 +27,16 @@ interface IAppSearchManager { /** * Sets the schema. * * @param schemaBytes Serialized SchemaProto. * @param schemaBundles List of AppSearchSchema bundles. * @param forceOverride Whether to apply the new schema even if it is incompatible. All * incompatible documents will be deleted. * @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link Void}>>. * The results of the call. */ void setSchema( in byte[] schemaBytes, boolean forceOverride, in AndroidFuture<AppSearchResult> callback); in List<Bundle> schemaBundles, boolean forceOverride, in AndroidFuture<AppSearchResult> callback); /** * Inserts documents into the index. Loading
apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java +1 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.app.appsearch; import android.annotation.IntDef; import android.annotation.NonNull; import android.app.appsearch.exceptions.IllegalSearchSpecException; import com.google.android.icing.proto.ResultSpecProto; import com.google.android.icing.proto.ScoringSpecProto; Loading
apex/appsearch/framework/java/android/app/appsearch/IllegalSchemaException.java→apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java +4 −2 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * 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. Loading @@ -14,16 +14,18 @@ * limitations under the License. */ package android.app.appsearch; package android.app.appsearch.exceptions; import android.annotation.NonNull; /** * Indicates that a {@link android.app.appsearch.AppSearchSchema} has logical inconsistencies such * as unpopulated mandatory fields or illegal combinations of parameters. * * @hide */ public class IllegalSchemaException extends IllegalArgumentException { /** * Constructs a new {@link IllegalSchemaException}. Loading