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

Commit d73e7d79 authored by Oluwarotimi Adesina's avatar Oluwarotimi Adesina
Browse files

Allow Parcelling GD over binder limit

Flag: android.app.appfunctions.flags.enable_app_function_manager
Test: atest CtsAppFunctionTestCases
Bug: 357551503
Change-Id: I9bae585a9b7117d4138a82df034668ada5d02f64
parent b9108227
Loading
Loading
Loading
Loading
+12 −9
Original line number Diff line number Diff line
@@ -40,8 +40,8 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
                public ExecuteAppFunctionRequest createFromParcel(Parcel parcel) {
                    String targetPackageName = parcel.readString8();
                    String functionIdentifier = parcel.readString8();
                    GenericDocument parameters;
                    parameters = GenericDocument.createFromParcel(parcel);
                    GenericDocumentWrapper parameters = GenericDocumentWrapper
                            .CREATOR.createFromParcel(parcel);
                    Bundle extras = parcel.readBundle(Bundle.class.getClassLoader());
                    return new ExecuteAppFunctionRequest(
                            targetPackageName, functionIdentifier, extras, parameters);
@@ -75,17 +75,17 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
     *
     * <p>The document may have missing parameters. Developers are advised to implement defensive
     * handling measures.
     *
     * <p>
     * TODO(b/357551503): Document how function parameters can be obtained for function execution
     */
    @NonNull
    private final GenericDocument mParameters;
    private final GenericDocumentWrapper mParameters;

    private ExecuteAppFunctionRequest(
            @NonNull String targetPackageName,
            @NonNull String functionIdentifier,
            @NonNull Bundle extras,
            @NonNull GenericDocument parameters) {
            @NonNull GenericDocumentWrapper parameters) {
        mTargetPackageName = Objects.requireNonNull(targetPackageName);
        mFunctionIdentifier = Objects.requireNonNull(functionIdentifier);
        mExtras = Objects.requireNonNull(extras);
@@ -117,7 +117,7 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
     */
    @NonNull
    public GenericDocument getParameters() {
        return mParameters;
        return mParameters.getValue();
    }

    /**
@@ -152,7 +152,8 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
        @NonNull
        private Bundle mExtras = Bundle.EMPTY;
        @NonNull
        private GenericDocument mParameters = new GenericDocument.Builder<>("", "", "").build();
        private GenericDocument mParameters =
                new GenericDocument.Builder<>("", "", "").build();

        public Builder(@NonNull String targetPackageName, @NonNull String functionIdentifier) {
            mTargetPackageName = Objects.requireNonNull(targetPackageName);
@@ -173,7 +174,8 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
         */
        @NonNull
        public Builder setParameters(@NonNull GenericDocument parameters) {
            mParameters = Objects.requireNonNull(parameters);
            Objects.requireNonNull(parameters);
            mParameters = parameters;
            return this;
        }

@@ -183,7 +185,8 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
        @NonNull
        public ExecuteAppFunctionRequest build() {
            return new ExecuteAppFunctionRequest(
                    mTargetPackageName, mFunctionIdentifier, mExtras, mParameters);
                    mTargetPackageName, mFunctionIdentifier, mExtras,
                    new GenericDocumentWrapper(mParameters));
        }
    }
}
+30 −23
Original line number Diff line number Diff line
@@ -41,13 +41,16 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
            new Creator<ExecuteAppFunctionResponse>() {
                @Override
                public ExecuteAppFunctionResponse createFromParcel(Parcel parcel) {
                    GenericDocument result =
                            Objects.requireNonNull(GenericDocument.createFromParcel(parcel));
                    GenericDocumentWrapper resultWrapper =
                            Objects.requireNonNull(
                                    GenericDocumentWrapper
                                            .CREATOR.createFromParcel(parcel));
                    Bundle extras = Objects.requireNonNull(
                            parcel.readBundle(Bundle.class.getClassLoader()));
                    int resultCode = parcel.readInt();
                    String errorMessage = parcel.readString8();
                    return new ExecuteAppFunctionResponse(result, extras, resultCode, errorMessage);
                    return new ExecuteAppFunctionResponse(
                            resultWrapper, extras, resultCode, errorMessage);
                }

                @Override
@@ -127,7 +130,7 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
     * <p>See {@link #getResultDocument} for more information on extracting the return value.
     */
    @NonNull
    private final GenericDocument mResultDocument;
    private final GenericDocumentWrapper mResultDocumentWrapper;

    /**
     * Returns the additional metadata data relevant to this function execution response.
@@ -135,16 +138,29 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
    @NonNull
    private final Bundle mExtras;

    private ExecuteAppFunctionResponse(@NonNull GenericDocument resultDocument,
    private ExecuteAppFunctionResponse(@NonNull GenericDocumentWrapper resultDocumentWrapper,
                                       @NonNull Bundle extras,
                                       @ResultCode int resultCode,
                                       @Nullable String errorMessage) {
        mResultDocument = Objects.requireNonNull(resultDocument);
        mResultDocumentWrapper = Objects.requireNonNull(resultDocumentWrapper);
        mExtras = Objects.requireNonNull(extras);
        mResultCode = resultCode;
        mErrorMessage = errorMessage;
    }

    /**
     * Returns result codes from throwable.
     *
     * @hide
     */
    @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
    static @ResultCode int getResultCode(@NonNull Throwable t) {
        if (t instanceof IllegalArgumentException) {
            return ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT;
        }
        return ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR;
    }

    /**
     * Returns a generic document containing the return value of the executed function.
     *
@@ -166,7 +182,7 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
     */
    @NonNull
    public GenericDocument getResultDocument() {
        return mResultDocument;
        return mResultDocumentWrapper.getValue();
    }

    /**
@@ -210,7 +226,7 @@ public final class ExecuteAppFunctionResponse implements Parcelable {

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        mResultDocument.writeToParcel(dest, flags);
        mResultDocumentWrapper.writeToParcel(dest, flags);
        dest.writeBundle(mExtras);
        dest.writeInt(mResultCode);
        dest.writeString8(mErrorMessage);
@@ -235,25 +251,14 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
    public @interface ResultCode {
    }

    /**
     * Returns result codes from throwable.
     *
     * @hide
     */
    static @ResultCode int getResultCode(@NonNull Throwable t) {
        if (t instanceof IllegalArgumentException) {
            return ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT;
        }
        return ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR;
    }

    /**
     * The builder for creating {@link ExecuteAppFunctionResponse} instances.
     */
    @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
    public static final class Builder {
        @NonNull
        private GenericDocument mResultDocument = new GenericDocument.Builder<>("", "", "").build();
        private GenericDocument mResultDocument =
                new GenericDocument.Builder<>("", "", "").build();
        @NonNull
        private Bundle mExtras = Bundle.EMPTY;
        private int mResultCode;
@@ -271,7 +276,8 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
         * with a result code of {@link #RESULT_OK} and a resultDocument.
         */
        public Builder(@NonNull GenericDocument resultDocument) {
            mResultDocument = Objects.requireNonNull(resultDocument);
            Objects.requireNonNull(resultDocument);
            mResultDocument = resultDocument;
            mResultCode = RESULT_OK;
        }

@@ -300,7 +306,8 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
        @NonNull
        public ExecuteAppFunctionResponse build() {
            return new ExecuteAppFunctionResponse(
                    mResultDocument, mExtras, mResultCode, mErrorMessage);
                    new GenericDocumentWrapper(mResultDocument),
                    mExtras, mResultCode, mErrorMessage);
        }
    }
}
+91 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.appfunctions;

import android.app.appsearch.GenericDocument;
import android.os.Parcel;
import android.os.Parcelable;

import androidx.annotation.NonNull;

import java.util.Objects;

/**
 * The Parcelable object contains a {@link GenericDocument} to allow the parcelization of it
 * exceeding the binder limit.
 *
 * <p>{#link {@link Parcel#writeBlob(byte[])}} could take care of whether to pass data via binder
 * directly or Android shared memory if the data is large.
 *
 * @hide
 * @see Parcel#writeBlob(byte[])
 */
public final class GenericDocumentWrapper implements Parcelable {
    public static final Creator<GenericDocumentWrapper> CREATOR =
            new Creator<>() {
                @Override
                public GenericDocumentWrapper createFromParcel(Parcel in) {
                    byte[] dataBlob = Objects.requireNonNull(in.readBlob());
                    Parcel unmarshallParcel = Parcel.obtain();
                    try {
                        unmarshallParcel.unmarshall(dataBlob, 0, dataBlob.length);
                        unmarshallParcel.setDataPosition(0);
                        return new GenericDocumentWrapper(
                                GenericDocument.createFromParcel(unmarshallParcel));
                    } finally {
                        unmarshallParcel.recycle();
                    }
                }

                @Override
                public GenericDocumentWrapper[] newArray(int size) {
                    return new GenericDocumentWrapper[size];
                }
            };
    @NonNull
    private final GenericDocument mGenericDocument;

    public GenericDocumentWrapper(@NonNull GenericDocument genericDocument) {
        mGenericDocument = Objects.requireNonNull(genericDocument);
    }

    /**
     * Returns the wrapped {@link android.app.appsearch.GenericDocument}
     */
    @NonNull
    public GenericDocument getValue() {
        return mGenericDocument;
    }

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

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        Parcel parcel = Parcel.obtain();
        try {
            mGenericDocument.writeToParcel(parcel, flags);
            byte[] bytes = parcel.marshall();
            dest.writeBlob(bytes);
        } finally {
            parcel.recycle();
        }

    }
}