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

Commit 7ca88212 authored by Alexander Dorokhine's avatar Alexander Dorokhine Committed by Android (Google) Code Review
Browse files

Merge "Switch AppSearchBatchResult from Throwable to error code and message."

parents 72b2fcbc 93d53122
Loading
Loading
Loading
Loading
+41 −40
Original line number Diff line number Diff line
@@ -26,30 +26,32 @@ import java.util.Collections;
import java.util.Map;

/**
 * Provides access to multiple results from a batch operation accepting multiple inputs.
 * Provides access to multiple {@link AppSearchResult}s from a batch operation accepting multiple
 * inputs.
 *
 * @param <KeyType> The type of the keys for {@link #getResults} and {@link #getFailures}.
 * @param <KeyType> The type of the keys for {@link #getSuccesses} 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;
    @NonNull private final Map<KeyType, AppSearchResult<ValueType>> mSuccesses;
    @NonNull private final Map<KeyType, AppSearchResult<ValueType>> mFailures;

    private AppSearchBatchResult(
            @NonNull Map<KeyType, ValueType> results, @NonNull Map<KeyType, Throwable> failures) {
        mResults = results;
            @NonNull Map<KeyType, AppSearchResult<ValueType>> successes,
            @NonNull Map<KeyType, AppSearchResult<ValueType>> failures) {
        mSuccesses = successes;
        mFailures = failures;
    }

    private AppSearchBatchResult(@NonNull Parcel in) {
        mResults = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null));
        mSuccesses = 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(mSuccesses);
        dest.writeMap(mFailures);
    }

@@ -59,23 +61,24 @@ public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
    }

    /**
     * Returns a {@link Map} of all successful keys mapped to the results they produced.
     * Returns a {@link Map} of all successful keys mapped to the successful
     * {@link AppSearchResult}s they produced.
     *
     * <p>The values of the {@link Map} may be {@code null}.
     * <p>The values of the {@link Map} will not be {@code null}.
     */
    @NonNull
    public Map<KeyType, ValueType> getResults() {
        return mResults;
    public Map<KeyType, AppSearchResult<ValueType>> getSuccesses() {
        return mSuccesses;
    }

    /**
     * Returns a {@link Map} of all failed keys mapped to a {@link Throwable} representing the cause
     * of failure.
     * Returns a {@link Map} of all failed keys mapped to the failed {@link AppSearchResult}s they
     * produced.
     *
     * <p>The values of the {@link Map} may be {@code null}.
     * <p>The values of the {@link Map} will not be {@code null}.
     */
    @NonNull
    public Map<KeyType, Throwable> getFailures() {
    public Map<KeyType, AppSearchResult<ValueType>> getFailures() {
        return mFailures;
    }

@@ -99,15 +102,6 @@ public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
        }
    };

    /**
     * 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.
     *
@@ -116,35 +110,42 @@ public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
     * @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 final Map<KeyType, AppSearchResult<ValueType>> mSuccesses = new ArrayMap<>();
        private final Map<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();

        private Builder() {}
        /** Creates a new {@link Builder} for this {@link AppSearchBatchResult}. */
        public 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.
         * Associates the {@code key} with the given successful return value.
         *
         * <p>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;
        public Builder setSuccess(@NonNull KeyType key, @Nullable ValueType result) {
            return setResult(key, AppSearchResult.newSuccessfulResult(result));
        }

        /**
         * Registers that the {@code key} failed and associates it with {@code throwable}. Any
         * previous mapping for a key, whether success or failure, is deleted.
         * Associates the {@code key} with the given {@code result}.
         *
         * <p>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);
        @NonNull
        public Builder setResult(@NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
            if (result.isSuccess()) {
                mSuccesses.put(key, result);
                mFailures.remove(key);
            } else {
                mFailures.put(key, result);
                mSuccesses.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);
            return new AppSearchBatchResult<>(mSuccesses, mFailures);
        }
    }
}
+19 −15
Original line number Diff line number Diff line
@@ -75,9 +75,7 @@ public class AppSearchManager {
     *             REPEATED} property.
     * </ul>
     *
     * <p>The following types of schema changes are not backwards-compatible. Supplying a schema
     * with such changes will result in this call throwing an {@link IllegalSchemaException}
     * describing the incompatibility, and the previously set schema will remain active:
     * <p>The following types of schema changes are not backwards-compatible:
     * <ul>
     *     <li>Removal of an existing type
     *     <li>Removal of a property from a type
@@ -93,6 +91,10 @@ public class AppSearchManager {
     *         {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED
     *             REQUIRED} property.
     * </ul>
     * <p>Supplying a schema with such changes will result in this call returning an
     * {@link AppSearchResult} with a code of {@link AppSearchResult#RESULT_INVALID_SCHEMA} and an
     * error message describing the incompatibility. In this case the previously set schema will
     * remain active.
     *
     * <p>If you need to make non-backwards-compatible changes as described above, instead use the
     * {@link #setSchema(List, boolean)} method with the {@code forceOverride} parameter set to
@@ -102,13 +104,13 @@ public class AppSearchManager {
     * efficiently.
     *
     * @param schemas The schema configs for the types used by the calling app.
     * @throws IllegalSchemaException If the provided schema is invalid, or is incompatible with the
     *     previous schema.
     * @return the result of performing this operation.
     *
     * @hide
     */
    public void setSchema(@NonNull AppSearchSchema... schemas) {
        setSchema(Arrays.asList(schemas), /*forceOverride=*/false);
    @NonNull
    public AppSearchResult<Void> setSchema(@NonNull AppSearchSchema... schemas) {
        return setSchema(Arrays.asList(schemas), /*forceOverride=*/false);
    }

    /**
@@ -116,20 +118,22 @@ public class AppSearchManager {
     *
     * <p>This method is similar to {@link #setSchema(AppSearchSchema...)}, except for the
     * {@code forceOverride} parameter. If a backwards-incompatible schema is specified but the
     * {@code forceOverride} parameter is set to {@code true}, instead of throwing an
     * {@link IllegalSchemaException}, all documents which are not compatible with the new schema
     * will be deleted and the incompatible schema will be applied.
     * {@code forceOverride} parameter is set to {@code true}, instead of returning an
     * {@link AppSearchResult} with the {@link AppSearchResult#RESULT_INVALID_SCHEMA} code, all
     * documents which are not compatible with the new schema will be deleted and the incompatible
     * schema will be applied.
     *
     * @param schemas The schema configs for the types used by the calling app.
     * @param forceOverride Whether to force the new schema to be applied even if there are
     *     incompatible changes versus the previously set schema. Documents which are incompatible
     *     with the new schema will be deleted.
     * @throws IllegalSchemaException If the provided schema is invalid, or is incompatible with the
     *     previous schema and the {@code forceOverride} parameter is set to {@code false}.
     * @return the result of performing this operation.
     *
     * @hide
     */
    public void setSchema(@NonNull List<AppSearchSchema> schemas, boolean forceOverride) {
    @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) {
@@ -140,13 +144,13 @@ public class AppSearchManager {
        // TODO: This should use com.android.internal.infra.RemoteStream or another mechanism to
        //  avoid binder limits.
        byte[] schemaBytes = schemaProtoBuilder.build().toByteArray();
        AndroidFuture<Void> future = new AndroidFuture<>();
        AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
        try {
            mService.setSchema(schemaBytes, forceOverride, future);
        } catch (RemoteException e) {
            future.completeExceptionally(e);
        }
        getFutureOrThrow(future);
        return getFutureOrThrow(future);
    }

    /**
+216 −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.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;

/**
 * Information about the success or failure of an AppSearch call.
 *
 * @param <ValueType> The type of result object for successful calls.
 * @hide
 */
public class AppSearchResult<ValueType> implements Parcelable {
    /** Result codes from {@link AppSearchManager} methods. */
    @IntDef(prefix = {"RESULT_"}, value = {
            RESULT_OK,
            RESULT_UNKNOWN_ERROR,
            RESULT_INTERNAL_ERROR,
            RESULT_INVALID_ARGUMENT,
            RESULT_IO_ERROR,
            RESULT_OUT_OF_SPACE,
            RESULT_NOT_FOUND,
            RESULT_INVALID_SCHEMA,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ResultCode {}

    /** The call was successful. */
    public static final int RESULT_OK = 0;

    /** An unknown error occurred while processing the call. */
    public static final int RESULT_UNKNOWN_ERROR = 1;

    /**
     * An internal error occurred within AppSearch, which the caller cannot address.
     *
     *
     * This error may be considered similar to {@link IllegalStateException}
     */
    public static final int RESULT_INTERNAL_ERROR = 2;

    /**
     * The caller supplied invalid arguments to the call.
     *
     * This error may be considered similar to {@link IllegalArgumentException}.
     */
    public static final int RESULT_INVALID_ARGUMENT = 3;

    /**
     * An issue occurred reading or writing to storage. The call might succeed if repeated.
     *
     * This error may be considered similar to {@link java.io.IOException}.
     */
    public static final int RESULT_IO_ERROR = 4;

    /** Storage is out of space, and no more space could be reclaimed. */
    public static final int RESULT_OUT_OF_SPACE = 5;

    /** An entity the caller requested to interact with does not exist in the system. */
    public static final int RESULT_NOT_FOUND = 6;

    /** The caller supplied a schema which is invalid or incompatible with the previous schema. */
    public static final int RESULT_INVALID_SCHEMA = 7;

    private final @ResultCode int mResultCode;
    @Nullable private final ValueType mResultValue;
    @Nullable private final String mErrorMessage;

    private AppSearchResult(
            @ResultCode int resultCode,
            @Nullable ValueType resultValue,
            @Nullable String errorMessage) {
        mResultCode = resultCode;
        mResultValue = resultValue;
        mErrorMessage = errorMessage;
    }

    private AppSearchResult(@NonNull Parcel in) {
        mResultCode = in.readInt();
        mResultValue = (ValueType) in.readValue(/*loader=*/ null);
        mErrorMessage = in.readString();
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeInt(mResultCode);
        dest.writeValue(mResultValue);
        dest.writeString(mErrorMessage);
    }

    /** Returns {@code true} if {@link #getResultCode} equals {@link AppSearchResult#RESULT_OK}. */
    public boolean isSuccess() {
        return getResultCode() == RESULT_OK;
    }

    /** Returns one of the {@code RESULT} constants defined in {@link AppSearchResult}. */
    public @ResultCode int getResultCode() {
        return mResultCode;
    }

    /**
     * Returns the returned value associated with this result.
     *
     * <p>If {@link #isSuccess} is {@code false}, the result value is always {@code null}. The value
     * may be {@code null} even if {@link #isSuccess} is {@code true}. See the documentation of the
     * particular {@link AppSearchManager} call producing this {@link AppSearchResult} for what is
     * returned by {@link #getResultValue}.
     */
    @Nullable
    public ValueType getResultValue() {
        return mResultValue;
    }

    /**
     * Returns the error message associated with this result.
     *
     * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}. The error
     * message may be {@code null} even if {@link #isSuccess} is {@code false}. See the
     * documentation of the particular {@link AppSearchManager} call producing this
     * {@link AppSearchResult} for what is returned by {@link #getErrorMessage}.
     */
    @Nullable
    public String getErrorMessage() {
        return mErrorMessage;
    }

    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof AppSearchResult)) {
            return false;
        }
        AppSearchResult<?> otherResult = (AppSearchResult) other;
        return mResultCode == otherResult.mResultCode
                && Objects.equals(mResultValue, otherResult.mResultValue)
                && Objects.equals(mErrorMessage, otherResult.mErrorMessage);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mResultCode, mResultValue, mErrorMessage);
    }

    @Override
    @NonNull
    public String toString() {
        if (isSuccess()) {
            return "AppSearchResult [SUCCESS]: " + mResultValue;
        }
        return "AppSearchResult [FAILURE(" + mResultCode + ")]: " + mErrorMessage;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<AppSearchResult> CREATOR =
            new Creator<AppSearchResult>() {
        @NonNull
        @Override
        public AppSearchResult createFromParcel(@NonNull Parcel in) {
            return new AppSearchResult(in);
        }

        @NonNull
        @Override
        public AppSearchResult[] newArray(int size) {
            return new AppSearchResult[size];
        }
    };

    /**
     * Creates a new successful {@link AppSearchResult}.
     * @hide
     */
    @NonNull
    public static <ValueType> AppSearchResult<ValueType> newSuccessfulResult(
            @Nullable ValueType value) {
        return new AppSearchResult<>(RESULT_OK, value, /*errorMessage=*/ null);
    }

    /**
     * Creates a new failed {@link AppSearchResult}.
     * @hide
     */
    @NonNull
    public static <ValueType> AppSearchResult<ValueType> newFailedResult(
            @ResultCode int resultCode, @Nullable String errorMessage) {
        return new AppSearchResult<>(resultCode, /*resultValue=*/ null, errorMessage);
    }
}
+5 −4
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package android.app.appsearch;

import com.android.internal.infra.AndroidFuture;

parcelable AppSearchResult;
parcelable AppSearchBatchResult;

/** {@hide} */
@@ -27,11 +28,11 @@ interface IAppSearchManager {
     * @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.
     * @param callback {@link AndroidFuture}&lt;{@link AppSearchResult}&lt;{@link Void}&gt&gt;.
     *     The results of the call.
     */
    void setSchema(in byte[] schemaBytes, boolean forceOverride, in AndroidFuture callback);
    void setSchema(
        in byte[] schemaBytes, boolean forceOverride, in AndroidFuture<AppSearchResult> callback);

    /**
     * Inserts documents into the index.
+57 −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 com.android.server.appsearch;

import android.annotation.Nullable;
import android.app.appsearch.AppSearchResult;

/**
 * An exception thrown by {@link com.android.server.appsearch.AppSearchManagerService} or a
 * subcomponent.
 *
 * <p>These exceptions can be converted into a failed {@link android.app.appsearch.AppSearchResult}
 * for propagating to the client.
 */
public class AppSearchException extends Exception {
    private final @AppSearchResult.ResultCode int mResultCode;

    /** Initializes an {@link com.android.server.appsearch.AppSearchException} with no message. */
    public AppSearchException(@AppSearchResult.ResultCode int resultCode) {
        this(resultCode, /*message=*/ null);
    }

    public AppSearchException(
            @AppSearchResult.ResultCode int resultCode, @Nullable String message) {
        this(resultCode, message, /*cause=*/ null);
    }

    public AppSearchException(
            @AppSearchResult.ResultCode int resultCode,
            @Nullable String message,
            @Nullable Throwable cause) {
        super(message, cause);
        mResultCode = resultCode;
    }

    /**
     * Converts this {@link java.lang.Exception} into a failed
     * {@link android.app.appsearch.AppSearchResult}
     */
    public <T> AppSearchResult<T> toAppSearchResult() {
        return AppSearchResult.newFailedResult(mResultCode, getMessage());
    }
}
Loading