Merge "Port the putDocuments() API to be synchronous."
This commit is contained in:
committed by
Android (Google) Code Review
commit
1fb565c88e
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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
|
||||
try {
|
||||
mService.putDocument(document.getProto().toByteArray(), future);
|
||||
} catch (RemoteException e) {
|
||||
future.completeExceptionally(e);
|
||||
break;
|
||||
}
|
||||
documentsBytes.add(document.getProto().toByteArray());
|
||||
}
|
||||
// TODO(b/147614371) Fix error report for multiple documents.
|
||||
future.whenCompleteAsync((noop, err) -> callback.accept(err), executor);
|
||||
AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>();
|
||||
try {
|
||||
mService.putDocuments(documentsBytes, future);
|
||||
} catch (RemoteException e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
return getFutureOrThrow(future);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,8 @@ package android.app.appsearch;
|
||||
|
||||
import com.android.internal.infra.AndroidFuture;
|
||||
|
||||
parcelable AppSearchBatchResult;
|
||||
|
||||
/** {@hide} */
|
||||
interface IAppSearchManager {
|
||||
/**
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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);
|
||||
impl.putDocument(callingUid, document);
|
||||
callback.complete(null);
|
||||
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);
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user