Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c457e57 authored by Alexander Dorokhine's avatar Alexander Dorokhine
Browse files

Implement putDocuments() support in AppSearchImpl.

Bug: 143789408
Test: AppSearchManagerTest
Change-Id: I4ef0540e0655a0b65a32b8d6a903ae955c687f4f
parent 1941c695
Loading
Loading
Loading
Loading
+60 −15
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.CurrentTimeMillisLong;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.appsearch.AppSearchSchema.PropertyConfig;
import android.os.Bundle;
import android.util.Log;

@@ -574,10 +575,6 @@ public final class AppSearch {
     * @hide
     */
    public static class Email extends Document {

        /** The name of the schema type for {@link Email} documents.*/
        public static final String SCHEMA_TYPE = "builtin:Email";

        private static final String KEY_FROM = "from";
        private static final String KEY_TO = "to";
        private static final String KEY_CC = "cc";
@@ -585,14 +582,53 @@ public final class AppSearch {
        private static final String KEY_SUBJECT = "subject";
        private static final String KEY_BODY = "body";

        /**
         * Creates a new {@link Email} from the contents of an existing {@link Document}.
         *
         * @param document The {@link Document} containing the email content.
         */
        public Email(@NonNull Document document) {
            super(document);
        }
        /** The name of the schema type for {@link Email} documents.*/
        public static final String SCHEMA_TYPE = "builtin:Email";

        public static final AppSearchSchema SCHEMA = AppSearchSchema.newBuilder(SCHEMA_TYPE)
                .addProperty(AppSearchSchema.newPropertyBuilder(KEY_FROM)
                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                        .build()

                ).addProperty(AppSearchSchema.newPropertyBuilder(KEY_TO)
                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                        .build()

                ).addProperty(AppSearchSchema.newPropertyBuilder(KEY_CC)
                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                        .build()

                ).addProperty(AppSearchSchema.newPropertyBuilder(KEY_BCC)
                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                        .build()

                ).addProperty(AppSearchSchema.newPropertyBuilder(KEY_SUBJECT)
                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                        .build()

                ).addProperty(AppSearchSchema.newPropertyBuilder(KEY_BODY)
                        .setDataType(PropertyConfig.DATA_TYPE_STRING)
                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
                        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
                        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
                        .build()

                ).build();

        /**
         * Creates a new {@link Email.Builder}.
@@ -603,6 +639,15 @@ public final class AppSearch {
            return new Builder(uri);
        }

        /**
         * Creates a new {@link Email} from the contents of an existing {@link Document}.
         *
         * @param document The {@link Document} containing the email content.
         */
        public Email(@NonNull Document document) {
            super(document);
        }

        /**
         * Get the from address of {@link Email}.
         *
@@ -615,10 +660,10 @@ public final class AppSearch {
        }

        /**
         * Get the destination address of {@link Email}.
         * Get the destination addresses of {@link Email}.
         *
         * @return Returns the destination address of {@link Email} or {@code null} if it's not been
         *         set yet.
         * @return Returns the destination addresses of {@link Email} or {@code null} if it's not
         *         been set yet.
         * @hide
         */
        @Nullable
+12 −12
Original line number Diff line number Diff line
@@ -54,7 +54,7 @@ public class AppSearchManager {
    }

    /**
     * Sets the schema being used by documents provided to the #put method.
     * Sets the schema being used by documents provided to the {@link #putDocuments} method.
     *
     * <p>The schema provided here is compared to the stored copy of the schema previously supplied
     * to {@link #setSchema}, if any, to determine how to treat existing documents. The following
@@ -106,13 +106,12 @@ public class AppSearchManager {
     *
     * @hide
     */
    // TODO(b/143789408): linkify #put after that API is created
    public void setSchema(@NonNull AppSearchSchema... schemas) {
        setSchema(Arrays.asList(schemas), /*forceOverride=*/false);
    }

    /**
     * Sets the schema being used by documents provided to the #put method.
     * Sets the schema being used by documents provided to the {@link #putDocuments} method.
     *
     * <p>This method is similar to {@link #setSchema(AppSearchSchema...)}, except for the
     * {@code forceOverride} parameter. If a backwards-incompatible schema is specified but the
@@ -129,7 +128,6 @@ public class AppSearchManager {
     *
     * @hide
     */
    // TODO(b/143789408): linkify #put after that API is created
    public void setSchema(@NonNull List<AppSearchSchema> schemas, boolean forceOverride) {
        // Prepare the merged schema for transmission.
        SchemaProto.Builder schemaProtoBuilder = SchemaProto.newBuilder();
@@ -151,26 +149,28 @@ public class AppSearchManager {
    }

    /**
     * Index {@link Document} to AppSearch
     * Index {@link android.app.appsearch.AppSearch.Document Documents} into AppSearch.
     *
     * <p>You should not call this method directly; instead, use the {@code AppSearch#put()} API
     * provided by JetPack.
     * <p>You should not call this method directly; instead, use the
     * {@code AppSearch#putDocuments()} API provided by JetPack.
     *
     * <p>The schema should be set via {@link #setSchema} method.
     * <p>Each {@link AppSearch.Document Document's} {@code schemaType} field must be set to the
     * name of a schema type previously registered via the {@link #setSchema} method.
     *
     * @param documents {@link Document Documents} that need to be indexed.
     * @param executor Executor on which to invoke the callback.
     * @param callback Callback to receive errors resulting from setting the schema. If the
     *                 operation succeeds, the callback will be invoked with {@code null}.
     * @param callback Callback to receive errors. On success, it will be called with {@code null}.
     *     On failure, it will be called with a {@link Throwable} describing the failure.
     */
    public void put(@NonNull List<Document> documents,
    public void putDocuments(
            @NonNull List<Document> documents,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<? super Throwable> callback) {
        AndroidFuture<Void> future = new AndroidFuture<>();
        for (Document document : documents) {
            // TODO(b/146386470) batching Document protos
            try {
                mService.put(document.getProto().toByteArray(), future);
                mService.putDocument(document.getProto().toByteArray(), future);
            } catch (RemoteException e) {
                future.completeExceptionally(e);
                break;
+13 −3
Original line number Diff line number Diff line
@@ -22,15 +22,25 @@ interface IAppSearchManager {
    /**
     * Sets the schema.
     *
     * @param schemaProto Serialized SchemaProto.
     * @param schemaBytes Serialized SchemaProto.
     * @param forceOverride Whether to apply the new schema even if it is incompatible. All
     *     incompatible documents will be deleted.
     * @param callback {@link AndroidFuture}&lt;{@link Void}&gt;. Will be completed with
     *     {@code null} upon successful completion of the setSchema call, or completed
     *     exceptionally if setSchema fails.
     */
    void setSchema(in byte[] schemaProto, boolean forceOverride, in AndroidFuture callback);
    void put(in byte[] documentBytes, in AndroidFuture callback);
    void setSchema(in byte[] schemaBytes, boolean forceOverride, in AndroidFuture callback);

    /**
     * Inserts a document into the index.
     *
     * @param documentBytes serialized DocumentProto
     * @param callback {@link AndroidFuture}&lt;{@link Void}&gt;. Will be completed with
     *     {@code null} upon successful completion of the put call, or completed exceptionally if
     *     put fails.
     */
    void putDocument(in byte[] documentBytes, in AndroidFuture callback);

    /**
     * Searches a document based on a given query string.
     *
+13 −2
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import com.android.server.appsearch.impl.AppSearchImpl;
import com.android.server.appsearch.impl.FakeIcing;
import com.android.server.appsearch.impl.ImplInstanceManager;

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;
@@ -71,11 +72,21 @@ public class AppSearchManagerService extends SystemService {
        }

        @Override
        public void put(byte[] documentBytes, AndroidFuture callback) {
        public void putDocument(byte[] documentBytes, AndroidFuture callback) {
            Preconditions.checkNotNull(documentBytes);
            Preconditions.checkNotNull(callback);
            int callingUid = Binder.getCallingUidOrThrow();
            int callingUserId = UserHandle.getUserId(callingUid);
            long callingIdentity = Binder.clearCallingIdentity();
            try {
                throw new UnsupportedOperationException("Put document not yet implemented");
                DocumentProto document = DocumentProto.parseFrom(documentBytes);
                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                impl.putDocument(callingUid, document);
                callback.complete(null);
            } catch (Throwable t) {
                callback.completeExceptionally(t);
            } finally {
                Binder.restoreCallingIdentity(callingIdentity);
            }
        }
        // TODO(sidchhabra):Init FakeIcing properly.
+56 −0
Original line number Diff line number Diff line
@@ -22,7 +22,9 @@ import android.content.Context;

import com.android.internal.annotations.VisibleForTesting;

import com.google.android.icing.proto.DocumentProto;
import com.google.android.icing.proto.PropertyConfigProto;
import com.google.android.icing.proto.PropertyProto;
import com.google.android.icing.proto.SchemaProto;
import com.google.android.icing.proto.SchemaTypeConfigProto;

@@ -94,6 +96,60 @@ public final class AppSearchImpl {
        }
    }

    /**
     * Adds a document to the AppSearch index.
     *
     * @param callingUid The uid of the app calling AppSearch.
     * @param origDocument The document to index.
     */
    public void putDocument(int callingUid, @NonNull DocumentProto origDocument) {
        // Rewrite the type names to include the app's prefix
        String typePrefix = getTypePrefix(callingUid);
        DocumentProto.Builder documentBuilder = origDocument.toBuilder();
        rewriteDocumentTypes(typePrefix, documentBuilder);
        mFakeIcing.put(documentBuilder.build());
    }

    /**
     * Rewrites all types mentioned anywhere in {@code documentBuilder} to prepend
     * {@code typePrefix}.
     *
     * @param typePrefix The prefix to add
     * @param documentBuilder The document to mutate
     */
    @VisibleForTesting
    void rewriteDocumentTypes(
            @NonNull String typePrefix,
            @NonNull DocumentProto.Builder documentBuilder) {
        // Rewrite the type name to include the app's prefix
        String newSchema = typePrefix + documentBuilder.getSchema();
        documentBuilder.setSchema(newSchema);

        // Add namespace. If we ever allow users to set their own namespaces, this will have
        // to change to prepend the prefix instead of setting the whole namespace. We will also have
        // to store the namespaces in a map similar to the type map so we can rewrite queries with
        // empty namespaces.
        documentBuilder.setNamespace(typePrefix);

        // Recurse into derived documents
        for (int propertyIdx = 0;
                propertyIdx < documentBuilder.getPropertiesCount();
                propertyIdx++) {
            int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount();
            if (documentCount > 0) {
                PropertyProto.Builder propertyBuilder =
                        documentBuilder.getProperties(propertyIdx).toBuilder();
                for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) {
                    DocumentProto.Builder derivedDocumentBuilder =
                            propertyBuilder.getDocumentValues(documentIdx).toBuilder();
                    rewriteDocumentTypes(typePrefix, derivedDocumentBuilder);
                    propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder);
                }
                documentBuilder.setProperties(propertyIdx, propertyBuilder);
            }
        }
    }

   /**
     * Returns a type prefix in a format like {@code com.example.package@1000/} or
     * {@code com.example.sharedname:5678@1000/}.
Loading