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

Commit 6fb90496 authored by Utkarsh Nigam's avatar Utkarsh Nigam
Browse files

Add CancellationSignal to AppFunctionManager API.

Marking the existing functions as deprecated and not removing them because we have a lot of code referencing these previous APIs and we will remove these once those references have been updated. Since the usage is in G3 we can't do it right away.

The sidecar update and logic to wrap cancellation signal with timeout and other checks will be added in later cls.

Change-Id: I48007082315a23b395d135b7b2130a05df82e9a4
Flag: android.app.appfunctions.flags.enable_app_function_manager
Test: atest CtsAppFunctionTestCases
Bug: 360864791
parent e8c0fd8f
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -8732,13 +8732,15 @@ package android.app.admin {
package android.app.appfunctions {
  @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 java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
    method @Deprecated @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 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 java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
  }
  @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 java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
    method @Deprecated @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
    method @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
    field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
  }
+63 −18
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
import android.content.Context;
import android.os.CancellationSignal;
import android.os.ICancellationSignal;
import android.os.RemoteException;

import java.util.Objects;
@@ -73,7 +75,43 @@ public final class AppFunctionManager {
     *     android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
     *     android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code
     *     ExecuteAppFunctionResponse.RESULT_DENIED}.
     * @deprecated Use {@link #executeAppFunction(ExecuteAppFunctionRequest, Executor,
     *     CancellationSignal, Consumer)} instead. This method will be removed once usage references
     *     are updated.
     */
    @RequiresPermission(
            anyOf = {
                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
                Manifest.permission.EXECUTE_APP_FUNCTIONS
            },
            conditional = true)
    @UserHandleAware
    @Deprecated
    public void executeAppFunction(
            @NonNull ExecuteAppFunctionRequest request,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
        executeAppFunction(request, executor, new CancellationSignal(), callback);
    }

    /**
     * Executes the app function.
     *
     * <p>Note: Applications can execute functions they define. To execute functions defined in
     * another component, apps would need to have {@code
     * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
     * android.permission.EXECUTE_APP_FUNCTIONS}.
     *
     * @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. 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}.
     */
    // TODO(b/357551503): Document the behavior when the cancellation signal is issued.
    // TODO(b/360864791): Document that apps can opt-out from being executed by callers with
    //   EXECUTE_APP_FUNCTIONS and how a caller knows whether a function is opted out.
    // TODO(b/357551503): Update documentation when get / set APIs are implemented that this will
@@ -88,6 +126,7 @@ public final class AppFunctionManager {
    public void executeAppFunction(
            @NonNull ExecuteAppFunctionRequest request,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull CancellationSignal cancellationSignal,
            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
        Objects.requireNonNull(request);
        Objects.requireNonNull(executor);
@@ -96,7 +135,9 @@ public final class AppFunctionManager {
        ExecuteAppFunctionAidlRequest aidlRequest =
                new ExecuteAppFunctionAidlRequest(
                        request, mContext.getUser(), mContext.getPackageName());

        try {
            ICancellationSignal cancellationTransport =
                    mService.executeAppFunction(
                            aidlRequest,
                            new IExecuteAppFunctionCallback.Stub() {
@@ -105,7 +146,8 @@ public final class AppFunctionManager {
                                    try {
                                        executor.execute(() -> callback.accept(result));
                                    } catch (RuntimeException e) {
                                // Ideally shouldn't happen since errors are wrapped into the
                                        // Ideally shouldn't happen since errors are wrapped into
                                        // the
                                        // response, but we catch it here for additional safety.
                                        callback.accept(
                                                ExecuteAppFunctionResponse.newFailure(
@@ -115,6 +157,9 @@ public final class AppFunctionManager {
                                    }
                                }
                            });
            if (cancellationTransport != null) {
                cancellationSignal.setRemote(cancellationTransport);
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+63 −1
Original line number Diff line number Diff line
@@ -29,7 +29,12 @@ import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.CancellationSignal;
import android.os.RemoteCallback;
import android.os.RemoteException;

import java.util.function.Consumer;

@@ -74,6 +79,7 @@ public abstract class AppFunctionService extends Service {
         */
        void perform(
                @NonNull ExecuteAppFunctionRequest request,
                @NonNull CancellationSignal cancellationSignal,
                @NonNull Consumer<ExecuteAppFunctionResponse> callback);
    }

@@ -85,6 +91,7 @@ public abstract class AppFunctionService extends Service {
            @Override
            public void executeAppFunction(
                    @NonNull ExecuteAppFunctionRequest request,
                    @NonNull ICancellationCallback cancellationCallback,
                    @NonNull IExecuteAppFunctionCallback callback) {
                if (context.checkCallingPermission(BIND_APP_FUNCTION_SERVICE)
                        == PERMISSION_DENIED) {
@@ -93,7 +100,10 @@ public abstract class AppFunctionService extends Service {
                SafeOneTimeExecuteAppFunctionCallback safeCallback =
                        new SafeOneTimeExecuteAppFunctionCallback(callback);
                try {
                    onExecuteFunction.perform(request, safeCallback::onResult);
                    onExecuteFunction.perform(
                            request,
                            buildCancellationSignal(cancellationCallback),
                            safeCallback::onResult);
                } catch (Exception ex) {
                    // Apps should handle exceptions. But if they don't, report the error on
                    // behalf of them.
@@ -105,6 +115,21 @@ public abstract class AppFunctionService extends Service {
        };
    }

    private static CancellationSignal buildCancellationSignal(
            @NonNull ICancellationCallback cancellationCallback) {
        final ICancellationSignal cancellationSignalTransport =
                CancellationSignal.createTransport();
        CancellationSignal cancellationSignal =
                CancellationSignal.fromTransport(cancellationSignalTransport);
        try {
            cancellationCallback.sendCancellationTransport(cancellationSignalTransport);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        return cancellationSignal ;
    }

    private final Binder mBinder = createBinder(
            AppFunctionService.this,
            AppFunctionService.this::onExecuteFunction);
@@ -115,6 +140,7 @@ public abstract class AppFunctionService extends Service {
        return mBinder;
    }


    /**
     * Called by the system to execute a specific app function.
     *
@@ -134,9 +160,45 @@ public abstract class AppFunctionService extends Service {
     *
     * @param request The function execution request.
     * @param callback A callback to report back the result.
     *
     * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, CancellationSignal,
     *     Consumer)} instead. This method will be removed once usage references are updated.
     */
    @MainThread
    @Deprecated
    public abstract void onExecuteFunction(
            @NonNull ExecuteAppFunctionRequest request,
            @NonNull Consumer<ExecuteAppFunctionResponse> callback);

    /**
     * Called by the system to execute a specific app function.
     *
     * <p>This method is triggered when the system requests your AppFunctionService to handle a
     * particular function you have registered and made available.
     *
     * <p>To ensure proper routing of function requests, assign a unique identifier to each
     * function. This identifier doesn't need to be globally unique, but it must be unique within
     * your app. For example, a function to order food could be identified as "orderFood". In most
     * cases this identifier should come from the ID automatically generated by the AppFunctions
     * SDK. You can determine the specific function to invoke by calling {@link
     * ExecuteAppFunctionRequest#getFunctionIdentifier()}.
     *
     * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker
     * thread and dispatch the result with the given callback. You should always report back the
     * result using the callback, no matter if the execution was successful or not.
     *
     * <p>This method also accepts a {@link CancellationSignal} that the app should listen to cancel
     * the execution of function if requested by the system.
     *
     * @param request The function execution request.
     * @param cancellationSignal A signal to cancel the execution.
     * @param callback A callback to report back the result.
     */
    @MainThread
    public void onExecuteFunction(
            @NonNull ExecuteAppFunctionRequest request,
            @NonNull CancellationSignal cancellationSignal,
            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
        onExecuteFunction(request, callback);
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.app.appfunctions;

import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
import android.app.appfunctions.IExecuteAppFunctionCallback;
import android.os.ICancellationSignal;

/**
 * Defines the interface for apps to interact with the app function execution service
@@ -32,7 +33,7 @@ interface IAppFunctionManager {
    * @param callback the callback to report the result.
    */
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = {android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional = true)")
    void executeAppFunction(
    ICancellationSignal executeAppFunction(
        in ExecuteAppFunctionAidlRequest request,
        in IExecuteAppFunctionCallback callback
    );
+3 −1
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@

package android.app.appfunctions;

import android.os.Bundle;
import android.app.appfunctions.ICancellationCallback;
import android.app.appfunctions.IExecuteAppFunctionCallback;
import android.app.appfunctions.ExecuteAppFunctionRequest;

@@ -34,10 +34,12 @@ oneway interface IAppFunctionService {
     * Called by the system to execute a specific app function.
     *
     * @param request  the function execution request.
     * @param cancellationCallback a callback to send back the cancellation transport.
     * @param callback a callback to report back the result.
     */
    void executeAppFunction(
        in ExecuteAppFunctionRequest request,
        in ICancellationCallback cancellationCallback,
        in IExecuteAppFunctionCallback callback
    );
}
Loading