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

Commit 2f8329c7 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Update executeAppFunction to take a OutcomeReceiver" into main

parents 3ea041fc fdd0e6bb
Loading
Loading
Loading
Loading
+27 −20
Original line number Diff line number Diff line
@@ -8792,8 +8792,31 @@ package android.app.admin {
package android.app.appfunctions {
  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionException extends java.lang.Exception implements android.os.Parcelable {
    ctor public AppFunctionException(int, @Nullable String);
    ctor public AppFunctionException(int, @Nullable String, @NonNull android.os.Bundle);
    method public int describeContents();
    method public int getErrorCategory();
    method public int getErrorCode();
    method @Nullable public String getErrorMessage();
    method @NonNull public android.os.Bundle getExtras();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.AppFunctionException> CREATOR;
    field public static final int ERROR_APP_UNKNOWN_ERROR = 3000; // 0xbb8
    field public static final int ERROR_CANCELLED = 2001; // 0x7d1
    field public static final int ERROR_CATEGORY_APP = 3; // 0x3
    field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1
    field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2
    field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
    field public static final int ERROR_DENIED = 1000; // 0x3e8
    field public static final int ERROR_DISABLED = 1002; // 0x3ea
    field public static final int ERROR_FUNCTION_NOT_FOUND = 1003; // 0x3eb
    field public static final int ERROR_INVALID_ARGUMENT = 1001; // 0x3e9
    field public static final int ERROR_SYSTEM_ERROR = 2000; // 0x7d0
  }
  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager {
    method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
    method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>);
    method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
    method public void isAppFunctionEnabled(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
    method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
@@ -8805,7 +8828,7 @@ package android.app.appfunctions {
  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public abstract class AppFunctionService extends android.app.Service {
    ctor public AppFunctionService();
    method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
    method @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
    method @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>);
    field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
  }
@@ -8827,30 +8850,14 @@ package android.app.appfunctions {
  }
  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class ExecuteAppFunctionResponse implements android.os.Parcelable {
    ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument);
    ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument, @NonNull android.os.Bundle);
    method public int describeContents();
    method public int getErrorCategory();
    method @Nullable public String getErrorMessage();
    method @NonNull public android.os.Bundle getExtras();
    method public int getResultCode();
    method @NonNull public android.app.appsearch.GenericDocument getResultDocument();
    method public boolean isSuccess();
    method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle);
    method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.ExecuteAppFunctionResponse> CREATOR;
    field public static final int ERROR_CATEGORY_APP = 3; // 0x3
    field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1
    field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2
    field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
    field public static final String PROPERTY_RETURN_VALUE = "android_app_appfunctions_returnvalue";
    field public static final int RESULT_APP_UNKNOWN_ERROR = 3000; // 0xbb8
    field public static final int RESULT_CANCELLED = 2001; // 0x7d1
    field public static final int RESULT_DENIED = 1000; // 0x3e8
    field public static final int RESULT_DISABLED = 1002; // 0x3ea
    field public static final int RESULT_FUNCTION_NOT_FOUND = 1003; // 0x3eb
    field public static final int RESULT_INVALID_ARGUMENT = 1001; // 0x3e9
    field public static final int RESULT_OK = 0; // 0x0
    field public static final int RESULT_SYSTEM_ERROR = 2000; // 0x7d0
  }
}
+21 −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.appfunctions.AppFunctionException;

parcelable AppFunctionException;
 No newline at end of file
+260 −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 static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;

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

/** Represents an app function related errors. */
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
public final class AppFunctionException extends Exception implements Parcelable {
    /**
     * The caller does not have the permission to execute an app function.
     *
     * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
     */
    public static final int ERROR_DENIED = 1000;

    /**
     * The caller supplied invalid arguments to the execution request.
     *
     * <p>This error may be considered similar to {@link IllegalArgumentException}.
     *
     * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
     */
    public static final int ERROR_INVALID_ARGUMENT = 1001;

    /**
     * The caller tried to execute a disabled app function.
     *
     * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
     */
    public static final int ERROR_DISABLED = 1002;

    /**
     * The caller tried to execute a function that does not exist.
     *
     * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
     */
    public static final int ERROR_FUNCTION_NOT_FOUND = 1003;

    /**
     * An internal unexpected error coming from the system.
     *
     * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
     */
    public static final int ERROR_SYSTEM_ERROR = 2000;

    /**
     * The operation was cancelled. Use this error code to report that a cancellation is done after
     * receiving a cancellation signal.
     *
     * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
     */
    public static final int ERROR_CANCELLED = 2001;

    /**
     * An unknown error occurred while processing the call in the AppFunctionService.
     *
     * <p>This error is thrown when the service is connected in the remote application but an
     * unexpected error is thrown from the bound application.
     *
     * <p>This error is in the {@link #ERROR_CATEGORY_APP} category.
     */
    public static final int ERROR_APP_UNKNOWN_ERROR = 3000;

    /**
     * The error category is unknown.
     *
     * <p>This is the default value for {@link #getErrorCategory}.
     */
    public static final int ERROR_CATEGORY_UNKNOWN = 0;

    /**
     * The error is caused by the app requesting a function execution.
     *
     * <p>For example, the caller provided invalid parameters in the execution request e.g. an
     * invalid function ID.
     *
     * <p>Errors in the category fall in the range 1000-1999 inclusive.
     */
    public static final int ERROR_CATEGORY_REQUEST_ERROR = 1;

    /**
     * The error is caused by an issue in the system.
     *
     * <p>For example, the AppFunctionService implementation is not found by the system.
     *
     * <p>Errors in the category fall in the range 2000-2999 inclusive.
     */
    public static final int ERROR_CATEGORY_SYSTEM = 2;

    /**
     * The error is caused by the app providing the function.
     *
     * <p>For example, the app crashed when the system is executing the request.
     *
     * <p>Errors in the category fall in the range 3000-3999 inclusive.
     */
    public static final int ERROR_CATEGORY_APP = 3;

    private final int mErrorCode;
    @Nullable private final String mErrorMessage;
    @NonNull private final Bundle mExtras;

    /**
     * @param errorCode The error code.
     * @param errorMessage The error message.
     */
    public AppFunctionException(@ErrorCode int errorCode, @Nullable String errorMessage) {
        this(errorCode, errorMessage, Bundle.EMPTY);
    }

    /**
     * @param errorCode The error code.
     * @param errorMessage The error message.
     * @param extras The extras associated with this error.
     */
    public AppFunctionException(
            @ErrorCode int errorCode, @Nullable String errorMessage, @NonNull Bundle extras) {
        mErrorCode = errorCode;
        mErrorMessage = errorMessage;
        mExtras = Objects.requireNonNull(extras);
    }

    private AppFunctionException(@NonNull Parcel in) {
        mErrorCode = in.readInt();
        mErrorMessage = in.readString8();
        mExtras = Objects.requireNonNull(in.readBundle(getClass().getClassLoader()));
    }

    /** Returns one of the {@code ERROR} constants. */
    @ErrorCode
    public int getErrorCode() {
        return mErrorCode;
    }

    /** Returns the error message. */
    @Nullable
    public String getErrorMessage() {
        return mErrorMessage;
    }

    /**
     * Returns the error category.
     *
     * <p>This method categorizes errors based on their underlying cause, allowing developers to
     * implement targeted error handling and provide more informative error messages to users. It
     * maps ranges of error codes to specific error categories.
     *
     * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the error code does not belong to
     * any error category.
     *
     * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding
     * error code ranges.
     */
    @ErrorCategory
    public int getErrorCategory() {
        if (mErrorCode >= 1000 && mErrorCode < 2000) {
            return ERROR_CATEGORY_REQUEST_ERROR;
        }
        if (mErrorCode >= 2000 && mErrorCode < 3000) {
            return ERROR_CATEGORY_SYSTEM;
        }
        if (mErrorCode >= 3000 && mErrorCode < 4000) {
            return ERROR_CATEGORY_APP;
        }
        return ERROR_CATEGORY_UNKNOWN;
    }

    /** Returns any extras associated with this error. */
    @NonNull
    public Bundle getExtras() {
        return mExtras;
    }

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

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeInt(mErrorCode);
        dest.writeString8(mErrorMessage);
        dest.writeBundle(mExtras);
    }

    /**
     * Error codes.
     *
     * @hide
     */
    @IntDef(
            prefix = {"ERROR_"},
            value = {
                ERROR_DENIED,
                ERROR_APP_UNKNOWN_ERROR,
                ERROR_FUNCTION_NOT_FOUND,
                ERROR_SYSTEM_ERROR,
                ERROR_INVALID_ARGUMENT,
                ERROR_DISABLED,
                ERROR_CANCELLED
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ErrorCode {}

    /**
     * Error categories.
     *
     * @hide
     */
    @IntDef(
            prefix = {"ERROR_CATEGORY_"},
            value = {
                ERROR_CATEGORY_UNKNOWN,
                ERROR_CATEGORY_REQUEST_ERROR,
                ERROR_CATEGORY_APP,
                ERROR_CATEGORY_SYSTEM
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ErrorCategory {}

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

                @Override
                public AppFunctionException[] newArray(int size) {
                    return new AppFunctionException[size];
                }
            };
}
+22 −16
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@

package android.app.appfunctions;

import static android.app.appfunctions.ExecuteAppFunctionResponse.getResultCode;
import static android.app.appfunctions.AppFunctionException.ERROR_SYSTEM_ERROR;
import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;

import android.Manifest;
@@ -39,7 +39,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * Provides access to app functions.
@@ -147,16 +146,16 @@ public final class AppFunctionManager {
     * @param request the request to execute the app function
     * @param executor the executor to run the callback
     * @param cancellationSignal the cancellation signal to cancel the execution.
     * @param callback the callback to receive the function execution result.
     * @param callback the callback to receive the function execution result or error.
     *     <p>If the calling app does not own the app function or does not have {@code
     *     android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
     *     android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code
     *     ExecuteAppFunctionResponse.RESULT_DENIED}.
     *     AppFunctionException.ERROR_DENIED}.
     *     <p>If the caller only has {@code android.permission.EXECUTE_APP_FUNCTIONS} but the
     *     function requires {@code android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED}, the execution
     *     result will contain {@code ExecuteAppFunctionResponse.RESULT_DENIED}
     *     result will contain {@code AppFunctionException.ERROR_DENIED}
     *     <p>If the function requested for execution is disabled, then the execution result will
     *     contain {@code ExecuteAppFunctionResponse.RESULT_DISABLED}
     *     contain {@code AppFunctionException.ERROR_DISABLED}
     *     <p>If the cancellation signal is issued, the operation is cancelled and no response is
     *     returned to the caller.
     */
@@ -171,7 +170,9 @@ public final class AppFunctionManager {
            @NonNull ExecuteAppFunctionRequest request,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull CancellationSignal cancellationSignal,
            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
            @NonNull
                    OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException>
                            callback) {
        Objects.requireNonNull(request);
        Objects.requireNonNull(executor);
        Objects.requireNonNull(callback);
@@ -186,20 +187,25 @@ public final class AppFunctionManager {
                            aidlRequest,
                            new IExecuteAppFunctionCallback.Stub() {
                                @Override
                                public void onResult(ExecuteAppFunctionResponse result) {
                                public void onSuccess(ExecuteAppFunctionResponse result) {
                                    try {
                                        executor.execute(() -> callback.accept(result));
                                        executor.execute(() -> callback.onResult(result));
                                    } catch (RuntimeException e) {
                                        // Ideally shouldn't happen since errors are wrapped into
                                        // the
                                        // response, but we catch it here for additional safety.
                                        callback.accept(
                                                ExecuteAppFunctionResponse.newFailure(
                                                        getResultCode(e),
                                                        e.getMessage(),
                                                        /* extras= */ null));
                                        // the response, but we catch it here for additional safety.
                                        executor.execute(
                                                () ->
                                                        callback.onError(
                                                                new AppFunctionException(
                                                                        ERROR_SYSTEM_ERROR,
                                                                        e.getMessage())));
                                    }
                                }

                                @Override
                                public void onError(AppFunctionException exception) {
                                    executor.execute(() -> callback.onError(exception));
                                }
                            });
            if (cancellationTransport != null) {
                cancellationSignal.setRemote(cancellationTransport);
+1 −3
Original line number Diff line number Diff line
@@ -213,9 +213,7 @@ public class AppFunctionRuntimeMetadata extends GenericDocument {
            setEnabled(original.getEnabled());
        }

        /**
         * Sets an indicator specifying the function enabled state.
         */
        /** Sets an indicator specifying the function enabled state. */
        @NonNull
        public Builder setEnabled(@EnabledState int enabledState) {
            if (enabledState != APP_FUNCTION_STATE_DEFAULT
Loading