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

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

Add AppFunction Execution API [AppFunctionManager#executeAppFunction]

Flag: android.app.appfunctions.flags.enable_app_function_manager
Test: added CTS
Bug: 357551503
Change-Id: I0afecf461c29216614d9c807ee8772d491242ca8
parent cf7b3c37
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -8722,6 +8722,7 @@ package android.app.admin {
package android.app.appfunctions {
package android.app.appfunctions {
  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager {
  @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>);
  }
  }
  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public abstract class AppFunctionService extends android.app.Service {
  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public abstract class AppFunctionService extends android.app.Service {
+75 −1
Original line number Original line Diff line number Diff line
@@ -16,11 +16,22 @@


package android.app.appfunctions;
package android.app.appfunctions;


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


import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
import android.content.Context;
import android.content.Context;
import android.os.RemoteException;

import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;


/**
/**
 * Provides app functions related functionalities.
 * Provides app functions related functionalities.
@@ -28,6 +39,7 @@ import android.content.Context;
 * <p>App function is a specific piece of functionality that an app offers to the system. These
 * <p>App function is a specific piece of functionality that an app offers to the system. These
 * functionalities can be integrated into various system features.
 * functionalities can be integrated into various system features.
 */
 */
// TODO(b/357551503): Implement get and set enabled app function APIs.
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
@SystemService(Context.APP_FUNCTION_SERVICE)
@SystemService(Context.APP_FUNCTION_SERVICE)
public final class AppFunctionManager {
public final class AppFunctionManager {
@@ -35,7 +47,10 @@ public final class AppFunctionManager {
    private final Context mContext;
    private final Context mContext;


    /**
    /**
     * TODO(b/357551503): add comments when implement this class
     * Creates an instance.
     *
     * @param mService An interface to the backing service.
     * @param context A {@link Context}.
     *
     *
     * @hide
     * @hide
     */
     */
@@ -43,4 +58,63 @@ public final class AppFunctionManager {
        this.mService = mService;
        this.mService = mService;
        this.mContext = context;
        this.mContext = context;
    }
    }

    /**
     * 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 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/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
    //   also return RESULT_DENIED if the app function is disabled.
    @RequiresPermission(
            anyOf = {Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
                    Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional = true)
    @UserHandleAware
    public void executeAppFunction(
            @NonNull ExecuteAppFunctionRequest request,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<ExecuteAppFunctionResponse> callback
    ) {
        Objects.requireNonNull(request);
        Objects.requireNonNull(executor);
        Objects.requireNonNull(callback);

        ExecuteAppFunctionAidlRequest aidlRequest =
                new ExecuteAppFunctionAidlRequest(
                        request,
                        mContext.getUser(),
                        mContext.getPackageName());
        try {
            mService.executeAppFunction(
                    aidlRequest,
                    new IExecuteAppFunctionCallback.Stub() {
                        @Override
                        public void onResult(ExecuteAppFunctionResponse result) {
                            try {
                                executor.execute(() -> callback.accept(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(new ExecuteAppFunctionResponse.Builder(
                                        getResultCode(e), e.getMessage()).build());
                            }
                        }
                    });
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }
}
}
+1 −7
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package android.app.appfunctions;
package android.app.appfunctions;


import static android.app.appfunctions.ExecuteAppFunctionResponse.getResultCode;
import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.Manifest.permission.BIND_APP_FUNCTION_SERVICE;
import static android.Manifest.permission.BIND_APP_FUNCTION_SERVICE;
@@ -86,13 +87,6 @@ public abstract class AppFunctionService extends Service {
                }
                }
            };
            };


    private static int getResultCode(@NonNull Throwable t) {
        if (t instanceof IllegalArgumentException) {
            return ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT;
        }
        return ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR;
    }

    @NonNull
    @NonNull
    @Override
    @Override
    public final IBinder onBind(@Nullable Intent intent) {
    public final IBinder onBind(@Nullable Intent intent) {
+12 −0
Original line number Original line Diff line number Diff line
@@ -235,6 +235,18 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
    public @interface ResultCode {
    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.
     * The builder for creating {@link ExecuteAppFunctionResponse} instances.
     */
     */