Loading apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java 0 → 100644 +150 −0 Original line number Diff line number Diff line /* * Copyright (C) 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.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; import java.util.Collections; import java.util.Map; /** * Provides access to multiple results from a batch operation accepting multiple inputs. * * @param <KeyType> The type of the keys for {@link #getResults} and {@link #getFailures}. * @param <ValueType> The type of result objects associated with the keys. * @hide */ public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { @NonNull private final Map<KeyType, ValueType> mResults; @NonNull private final Map<KeyType, Throwable> mFailures; private AppSearchBatchResult( @NonNull Map<KeyType, ValueType> results, @NonNull Map<KeyType, Throwable> failures) { mResults = results; mFailures = failures; } private AppSearchBatchResult(@NonNull Parcel in) { mResults = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null)); mFailures = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null)); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeMap(mResults); dest.writeMap(mFailures); } /** Returns {@code true} if this {@link AppSearchBatchResult} has no failures. */ public boolean isSuccess() { return mFailures.isEmpty(); } /** * Returns a {@link Map} of all successful keys mapped to the results they produced. * * <p>The values of the {@link Map} may be {@code null}. */ @NonNull public Map<KeyType, ValueType> getResults() { return mResults; } /** * Returns a {@link Map} of all failed keys mapped to a {@link Throwable} representing the cause * of failure. * * <p>The values of the {@link Map} may be {@code null}. */ @NonNull public Map<KeyType, Throwable> getFailures() { return mFailures; } @Override public int describeContents() { return 0; } public static final Creator<AppSearchBatchResult> CREATOR = new Creator<AppSearchBatchResult>() { @NonNull @Override public AppSearchBatchResult createFromParcel(@NonNull Parcel in) { return new AppSearchBatchResult(in); } @NonNull @Override public AppSearchBatchResult[] newArray(int size) { return new AppSearchBatchResult[size]; } }; /** * Creates a new {@link Builder} for this {@link AppSearchBatchResult}. * @hide */ @NonNull public static <KeyType, ValueType> Builder<KeyType, ValueType> newBuilder() { return new Builder<>(); } /** * Builder for {@link AppSearchBatchResult} objects. * * @param <KeyType> The type of keys. * @param <ValueType> The type of result objects associated with the keys. * @hide */ public static final class Builder<KeyType, ValueType> { @NonNull private final Map<KeyType, ValueType> mResults = new ArrayMap<>(); @NonNull private final Map<KeyType, Throwable> mFailures = new ArrayMap<>(); private Builder() {} /** * Registers that the {@code key} was processed successfully and associates it with * {@code value}. Any previous mapping for a key, whether success or failure, is deleted. */ public Builder setSuccess(@NonNull KeyType key, @Nullable ValueType value) { mResults.put(key, value); mFailures.remove(key); return this; } /** * Registers that the {@code key} failed and associates it with {@code throwable}. Any * previous mapping for a key, whether success or failure, is deleted. */ public Builder setFailure(@NonNull KeyType key, @Nullable Throwable throwable) { mFailures.put(key, throwable); mResults.remove(key); return this; } /** Builds an {@link AppSearchBatchResult} from the contents of this {@link Builder}. */ @NonNull public AppSearchBatchResult<KeyType, ValueType> build() { return new AppSearchBatchResult<>(mResults, mFailures); } } } apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +17 −18 Original line number Diff line number Diff line Loading @@ -29,12 +29,12 @@ import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.StatusProto; import com.google.android.icing.protobuf.InvalidProtocolBufferException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.function.BiConsumer; import java.util.function.Consumer; /** * This class provides access to the centralized AppSearch index maintained by the system. Loading Loading @@ -158,26 +158,25 @@ public class AppSearchManager { * 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. On success, it will be called with {@code null}. * On failure, it will be called with a {@link Throwable} describing the failure. * @return An {@link AppSearchBatchResult} mapping the document URIs to {@link Void} if they * were successfully indexed, or a {@link Throwable} describing the failure if they could * not be indexed. * @hide */ public void putDocuments( @NonNull List<Document> documents, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<? super Throwable> callback) { AndroidFuture<Void> future = new AndroidFuture<>(); public AppSearchBatchResult<String, Void> putDocuments(@NonNull List<Document> documents) { // TODO(b/146386470): Transmit these documents as a RemoteStream instead of sending them in // one big list. List<byte[]> documentsBytes = new ArrayList<>(documents.size()); for (Document document : documents) { // TODO(b/146386470) batching Document protos documentsBytes.add(document.getProto().toByteArray()); } AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>(); try { mService.putDocument(document.getProto().toByteArray(), future); mService.putDocuments(documentsBytes, future); } catch (RemoteException e) { future.completeExceptionally(e); break; } } // TODO(b/147614371) Fix error report for multiple documents. future.whenCompleteAsync((noop, err) -> callback.accept(err), executor); return getFutureOrThrow(future); } /** Loading apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +11 −6 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package android.app.appsearch; import com.android.internal.infra.AndroidFuture; parcelable AppSearchBatchResult; /** {@hide} */ interface IAppSearchManager { /** Loading @@ -32,14 +34,17 @@ interface IAppSearchManager { void setSchema(in byte[] schemaBytes, boolean forceOverride, in AndroidFuture callback); /** * Inserts a document into the index. * Inserts documents into the index. * * @param documentBytes serialized DocumentProto * @param callback {@link AndroidFuture}<{@link Void}>. Will be completed with * {@code null} upon successful completion of the put call, or completed exceptionally if * put fails. * @param documentsBytes {@link List}<byte[]> of serialized DocumentProtos. * @param callback * {@link AndroidFuture}<{@link AppSearchBatchResult}<{@link String}, {@link Void}>>. * If the call fails to start, {@code callback} will be completed exceptionally. Otherwise, * {@code callback} will be completed with an * {@link AppSearchBatchResult}<{@link String}, {@link Void}> * where the keys are document URIs, and the values are {@code null}. */ void putDocument(in byte[] documentBytes, in AndroidFuture callback); void putDocuments(in List documentsBytes, in AndroidFuture<AppSearchBatchResult> callback); /** * Searches a document based on a given query string. Loading apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +19 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.appsearch; import android.annotation.NonNull; import android.app.appsearch.AppSearchBatchResult; import android.app.appsearch.IAppSearchManager; import android.content.Context; import android.os.Binder; Loading @@ -34,6 +35,8 @@ import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SearchSpecProto; import com.google.android.icing.protobuf.InvalidProtocolBufferException; import java.util.List; /** * TODO(b/142567528): add comments when implement this class */ Loading Loading @@ -72,17 +75,28 @@ public class AppSearchManagerService extends SystemService { } @Override public void putDocument(byte[] documentBytes, AndroidFuture callback) { Preconditions.checkNotNull(documentBytes); public void putDocuments( List documentsBytes, AndroidFuture<AppSearchBatchResult> callback) { Preconditions.checkNotNull(documentsBytes); Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = UserHandle.getUserId(callingUid); long callingIdentity = Binder.clearCallingIdentity(); try { DocumentProto document = DocumentProto.parseFrom(documentBytes); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); AppSearchBatchResult.Builder<String, Void> resultBuilder = AppSearchBatchResult.newBuilder(); for (int i = 0; i < documentsBytes.size(); i++) { byte[] documentBytes = (byte[]) documentsBytes.get(i); DocumentProto document = DocumentProto.parseFrom(documentBytes); try { impl.putDocument(callingUid, document); callback.complete(null); resultBuilder.setSuccess(document.getUri(), /*value=*/ null); } catch (Throwable t) { resultBuilder.setFailure(document.getUri(), t); } } callback.complete(resultBuilder.build()); } catch (Throwable t) { callback.completeExceptionally(t); } finally { Loading Loading
apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java 0 → 100644 +150 −0 Original line number Diff line number Diff line /* * Copyright (C) 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.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; import java.util.Collections; import java.util.Map; /** * Provides access to multiple results from a batch operation accepting multiple inputs. * * @param <KeyType> The type of the keys for {@link #getResults} and {@link #getFailures}. * @param <ValueType> The type of result objects associated with the keys. * @hide */ public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { @NonNull private final Map<KeyType, ValueType> mResults; @NonNull private final Map<KeyType, Throwable> mFailures; private AppSearchBatchResult( @NonNull Map<KeyType, ValueType> results, @NonNull Map<KeyType, Throwable> failures) { mResults = results; mFailures = failures; } private AppSearchBatchResult(@NonNull Parcel in) { mResults = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null)); mFailures = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null)); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeMap(mResults); dest.writeMap(mFailures); } /** Returns {@code true} if this {@link AppSearchBatchResult} has no failures. */ public boolean isSuccess() { return mFailures.isEmpty(); } /** * Returns a {@link Map} of all successful keys mapped to the results they produced. * * <p>The values of the {@link Map} may be {@code null}. */ @NonNull public Map<KeyType, ValueType> getResults() { return mResults; } /** * Returns a {@link Map} of all failed keys mapped to a {@link Throwable} representing the cause * of failure. * * <p>The values of the {@link Map} may be {@code null}. */ @NonNull public Map<KeyType, Throwable> getFailures() { return mFailures; } @Override public int describeContents() { return 0; } public static final Creator<AppSearchBatchResult> CREATOR = new Creator<AppSearchBatchResult>() { @NonNull @Override public AppSearchBatchResult createFromParcel(@NonNull Parcel in) { return new AppSearchBatchResult(in); } @NonNull @Override public AppSearchBatchResult[] newArray(int size) { return new AppSearchBatchResult[size]; } }; /** * Creates a new {@link Builder} for this {@link AppSearchBatchResult}. * @hide */ @NonNull public static <KeyType, ValueType> Builder<KeyType, ValueType> newBuilder() { return new Builder<>(); } /** * Builder for {@link AppSearchBatchResult} objects. * * @param <KeyType> The type of keys. * @param <ValueType> The type of result objects associated with the keys. * @hide */ public static final class Builder<KeyType, ValueType> { @NonNull private final Map<KeyType, ValueType> mResults = new ArrayMap<>(); @NonNull private final Map<KeyType, Throwable> mFailures = new ArrayMap<>(); private Builder() {} /** * Registers that the {@code key} was processed successfully and associates it with * {@code value}. Any previous mapping for a key, whether success or failure, is deleted. */ public Builder setSuccess(@NonNull KeyType key, @Nullable ValueType value) { mResults.put(key, value); mFailures.remove(key); return this; } /** * Registers that the {@code key} failed and associates it with {@code throwable}. Any * previous mapping for a key, whether success or failure, is deleted. */ public Builder setFailure(@NonNull KeyType key, @Nullable Throwable throwable) { mFailures.put(key, throwable); mResults.remove(key); return this; } /** Builds an {@link AppSearchBatchResult} from the contents of this {@link Builder}. */ @NonNull public AppSearchBatchResult<KeyType, ValueType> build() { return new AppSearchBatchResult<>(mResults, mFailures); } } }
apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +17 −18 Original line number Diff line number Diff line Loading @@ -29,12 +29,12 @@ import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.StatusProto; import com.google.android.icing.protobuf.InvalidProtocolBufferException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.function.BiConsumer; import java.util.function.Consumer; /** * This class provides access to the centralized AppSearch index maintained by the system. Loading Loading @@ -158,26 +158,25 @@ public class AppSearchManager { * 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. On success, it will be called with {@code null}. * On failure, it will be called with a {@link Throwable} describing the failure. * @return An {@link AppSearchBatchResult} mapping the document URIs to {@link Void} if they * were successfully indexed, or a {@link Throwable} describing the failure if they could * not be indexed. * @hide */ public void putDocuments( @NonNull List<Document> documents, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<? super Throwable> callback) { AndroidFuture<Void> future = new AndroidFuture<>(); public AppSearchBatchResult<String, Void> putDocuments(@NonNull List<Document> documents) { // TODO(b/146386470): Transmit these documents as a RemoteStream instead of sending them in // one big list. List<byte[]> documentsBytes = new ArrayList<>(documents.size()); for (Document document : documents) { // TODO(b/146386470) batching Document protos documentsBytes.add(document.getProto().toByteArray()); } AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>(); try { mService.putDocument(document.getProto().toByteArray(), future); mService.putDocuments(documentsBytes, future); } catch (RemoteException e) { future.completeExceptionally(e); break; } } // TODO(b/147614371) Fix error report for multiple documents. future.whenCompleteAsync((noop, err) -> callback.accept(err), executor); return getFutureOrThrow(future); } /** Loading
apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +11 −6 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package android.app.appsearch; import com.android.internal.infra.AndroidFuture; parcelable AppSearchBatchResult; /** {@hide} */ interface IAppSearchManager { /** Loading @@ -32,14 +34,17 @@ interface IAppSearchManager { void setSchema(in byte[] schemaBytes, boolean forceOverride, in AndroidFuture callback); /** * Inserts a document into the index. * Inserts documents into the index. * * @param documentBytes serialized DocumentProto * @param callback {@link AndroidFuture}<{@link Void}>. Will be completed with * {@code null} upon successful completion of the put call, or completed exceptionally if * put fails. * @param documentsBytes {@link List}<byte[]> of serialized DocumentProtos. * @param callback * {@link AndroidFuture}<{@link AppSearchBatchResult}<{@link String}, {@link Void}>>. * If the call fails to start, {@code callback} will be completed exceptionally. Otherwise, * {@code callback} will be completed with an * {@link AppSearchBatchResult}<{@link String}, {@link Void}> * where the keys are document URIs, and the values are {@code null}. */ void putDocument(in byte[] documentBytes, in AndroidFuture callback); void putDocuments(in List documentsBytes, in AndroidFuture<AppSearchBatchResult> callback); /** * Searches a document based on a given query string. Loading
apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +19 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.appsearch; import android.annotation.NonNull; import android.app.appsearch.AppSearchBatchResult; import android.app.appsearch.IAppSearchManager; import android.content.Context; import android.os.Binder; Loading @@ -34,6 +35,8 @@ import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SearchSpecProto; import com.google.android.icing.protobuf.InvalidProtocolBufferException; import java.util.List; /** * TODO(b/142567528): add comments when implement this class */ Loading Loading @@ -72,17 +75,28 @@ public class AppSearchManagerService extends SystemService { } @Override public void putDocument(byte[] documentBytes, AndroidFuture callback) { Preconditions.checkNotNull(documentBytes); public void putDocuments( List documentsBytes, AndroidFuture<AppSearchBatchResult> callback) { Preconditions.checkNotNull(documentsBytes); Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = UserHandle.getUserId(callingUid); long callingIdentity = Binder.clearCallingIdentity(); try { DocumentProto document = DocumentProto.parseFrom(documentBytes); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); AppSearchBatchResult.Builder<String, Void> resultBuilder = AppSearchBatchResult.newBuilder(); for (int i = 0; i < documentsBytes.size(); i++) { byte[] documentBytes = (byte[]) documentsBytes.get(i); DocumentProto document = DocumentProto.parseFrom(documentBytes); try { impl.putDocument(callingUid, document); callback.complete(null); resultBuilder.setSuccess(document.getUri(), /*value=*/ null); } catch (Throwable t) { resultBuilder.setFailure(document.getUri(), t); } } callback.complete(resultBuilder.build()); } catch (Throwable t) { callback.completeExceptionally(t); } finally { Loading