summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt6
-rw-r--r--core/java/android/app/appfunctions/AppFunctionManager.java81
-rw-r--r--core/java/android/app/appfunctions/AppFunctionService.java64
-rw-r--r--core/java/android/app/appfunctions/IAppFunctionManager.aidl5
-rw-r--r--core/java/android/app/appfunctions/IAppFunctionService.aidl4
-rw-r--r--core/java/android/app/appfunctions/ICancellationCallback.aidl24
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java31
7 files changed, 188 insertions, 27 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 8eb881139b34..fb8fe488f51e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -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";
}
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index 4682f3d30e1e..216ba5d994ec 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -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,25 +135,31 @@ public final class AppFunctionManager {
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(
- ExecuteAppFunctionResponse.newFailure(
- getResultCode(e),
- e.getMessage(),
- /* extras= */ null));
- }
- }
- });
+ ICancellationSignal cancellationTransport =
+ 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(
+ ExecuteAppFunctionResponse.newFailure(
+ getResultCode(e),
+ e.getMessage(),
+ /* extras= */ null));
+ }
+ }
+ });
+ if (cancellationTransport != null) {
+ cancellationSignal.setRemote(cancellationTransport);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java
index 0d981ea5a679..8e417737515e 100644
--- a/core/java/android/app/appfunctions/AppFunctionService.java
+++ b/core/java/android/app/appfunctions/AppFunctionService.java
@@ -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);
+ }
}
diff --git a/core/java/android/app/appfunctions/IAppFunctionManager.aidl b/core/java/android/app/appfunctions/IAppFunctionManager.aidl
index 28827bb3052c..c63217ffe850 100644
--- a/core/java/android/app/appfunctions/IAppFunctionManager.aidl
+++ b/core/java/android/app/appfunctions/IAppFunctionManager.aidl
@@ -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,8 +33,8 @@ 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
);
-} \ No newline at end of file
+}
diff --git a/core/java/android/app/appfunctions/IAppFunctionService.aidl b/core/java/android/app/appfunctions/IAppFunctionService.aidl
index cc5a20cfa194..291f33ccb1b8 100644
--- a/core/java/android/app/appfunctions/IAppFunctionService.aidl
+++ b/core/java/android/app/appfunctions/IAppFunctionService.aidl
@@ -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
);
}
diff --git a/core/java/android/app/appfunctions/ICancellationCallback.aidl b/core/java/android/app/appfunctions/ICancellationCallback.aidl
new file mode 100644
index 000000000000..03235aca017a
--- /dev/null
+++ b/core/java/android/app/appfunctions/ICancellationCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.os.ICancellationSignal;
+
+/** {@hide} */
+oneway interface ICancellationCallback {
+ void sendCancellationTransport(in ICancellationSignal cancellationTransport);
+} \ No newline at end of file
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index c8f8c2a6b223..082459b8c863 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -21,11 +21,13 @@ import static com.android.server.appfunctions.AppFunctionExecutors.THREAD_POOL_E
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
+import android.app.appfunctions.AppFunctionService;
import android.app.appfunctions.AppFunctionStaticMetadataHelper;
import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
import android.app.appfunctions.ExecuteAppFunctionResponse;
import android.app.appfunctions.IAppFunctionManager;
import android.app.appfunctions.IAppFunctionService;
+import android.app.appfunctions.ICancellationCallback;
import android.app.appfunctions.IExecuteAppFunctionCallback;
import android.app.appfunctions.SafeOneTimeExecuteAppFunctionCallback;
import android.app.appsearch.AppSearchManager;
@@ -37,11 +39,15 @@ import android.app.appsearch.observer.SchemaChangeInfo;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
import com.android.server.SystemService.TargetUser;
import com.android.server.appfunctions.RemoteServiceCaller.RunServiceCallCallback;
import com.android.server.appfunctions.RemoteServiceCaller.ServiceUsageCompleteListener;
@@ -99,7 +105,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
}
@Override
- public void executeAppFunction(
+ public ICancellationSignal executeAppFunction(
@NonNull ExecuteAppFunctionAidlRequest requestInternal,
@NonNull IExecuteAppFunctionCallback executeAppFunctionCallback) {
Objects.requireNonNull(requestInternal);
@@ -120,11 +126,14 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
ExecuteAppFunctionResponse.RESULT_DENIED,
exception.getMessage(),
/* extras= */ null));
- return;
+ return null;
}
int callingUid = Binder.getCallingUid();
- int callingPid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+
+ ICancellationSignal localCancelTransport = CancellationSignal.createTransport();
+
THREAD_POOL_EXECUTOR.execute(
() -> {
try {
@@ -132,12 +141,14 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
requestInternal,
callingUid,
callingPid,
+ localCancelTransport,
safeExecuteAppFunctionCallback);
} catch (Exception e) {
safeExecuteAppFunctionCallback.onResult(
mapExceptionToExecuteAppFunctionResponse(e));
}
});
+ return localCancelTransport;
}
@WorkerThread
@@ -145,6 +156,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
ExecuteAppFunctionAidlRequest requestInternal,
int callingUid,
int callingPid,
+ ICancellationSignal localCancelTransport,
SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback) {
UserHandle targetUser = requestInternal.getUserHandle();
// TODO(b/354956319): Add and honor the new enterprise policies.
@@ -203,6 +215,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
requestInternal,
serviceIntent,
targetUser,
+ localCancelTransport,
safeExecuteAppFunctionCallback,
/* bindFlags= */ Context.BIND_AUTO_CREATE
| Context.BIND_FOREGROUND_SERVICE);
@@ -219,8 +232,19 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
@NonNull ExecuteAppFunctionAidlRequest requestInternal,
@NonNull Intent serviceIntent,
@NonNull UserHandle targetUser,
+ @NonNull ICancellationSignal cancellationSignalTransport,
@NonNull SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback,
int bindFlags) {
+ CancellationSignal cancellationSignal =
+ CancellationSignal.fromTransport(cancellationSignalTransport);
+ ICancellationCallback cancellationCallback =
+ new ICancellationCallback.Stub() {
+ @Override
+ public void sendCancellationTransport(
+ @NonNull ICancellationSignal cancellationTransport) {
+ cancellationSignal.setRemote(cancellationTransport);
+ }
+ };
boolean bindServiceResult =
mRemoteServiceCaller.runServiceCall(
serviceIntent,
@@ -236,6 +260,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
try {
service.executeAppFunction(
requestInternal.getClientRequest(),
+ cancellationCallback,
new IExecuteAppFunctionCallback.Stub() {
@Override
public void onResult(