| /* |
| * 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.appsearch.functions; |
| |
| import android.annotation.FlaggedApi; |
| import android.annotation.MainThread; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.Service; |
| import android.app.appsearch.AppSearchResult; |
| import android.app.appsearch.aidl.AppSearchResultParcel; |
| import android.app.appsearch.aidl.IAppFunctionService; |
| import android.app.appsearch.aidl.IAppSearchResultCallback; |
| import android.app.appsearch.flags.Flags; |
| import android.content.Intent; |
| import android.os.Binder; |
| import android.os.IBinder; |
| import android.os.Process; |
| |
| import java.util.function.Consumer; |
| |
| /** |
| * Abstract base class to provide app functions to the system. |
| * |
| * <p>Include the following in the manifest: |
| * |
| * <pre> |
| * {@literal |
| * <service android:name=".YourService" |
| * android:permission="android.permission.BIND_APP_FUNCTION_SERVICE"> |
| * <intent-filter> |
| * <action android:name="android.app.appsearch.functions.AppFunctionService" /> |
| * </intent-filter> |
| * </service> |
| * } |
| * </pre> |
| * |
| * @see AppFunctionManager |
| */ |
| @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS) |
| public abstract class AppFunctionService extends Service { |
| private static final String TAG = "AppSearchAppFunction"; |
| |
| /** |
| * The {@link Intent} that must be declared as handled by the service. To be supported, the |
| * service must also require the {@link AppFunctionManager#PERMISSION_BIND_APP_FUNCTION_SERVICE} |
| * permission so that other applications can not abuse it. |
| */ |
| @NonNull |
| public static final String SERVICE_INTERFACE = |
| "android.app.appsearch.functions.AppFunctionService"; |
| |
| private final Binder mBinder = |
| new IAppFunctionService.Stub() { |
| @Override |
| public void executeAppFunction( |
| @NonNull ExecuteAppFunctionRequest request, |
| @NonNull IAppSearchResultCallback callback) { |
| // TODO(b/327134039): Replace this check with the new permission |
| if (Binder.getCallingUid() != Process.SYSTEM_UID) { |
| throw new SecurityException("Can only be called by the system server"); |
| } |
| SafeOneTimeAppSearchResultCallback safeCallback = |
| new SafeOneTimeAppSearchResultCallback(callback); |
| try { |
| AppFunctionService.this.onExecuteFunction( |
| request, |
| appFunctionResult -> { |
| AppSearchResultParcel appSearchResultParcel; |
| // Create result from value in success case and errorMessage in |
| // failure case. |
| if (appFunctionResult.isSuccess()) { |
| appSearchResultParcel = |
| AppSearchResultParcel |
| .fromExecuteAppFunctionResponse( |
| appFunctionResult.getResultValue()); |
| } else { |
| appSearchResultParcel = |
| AppSearchResultParcel.fromFailedResult( |
| appFunctionResult); |
| } |
| safeCallback.onResult(appSearchResultParcel); |
| }); |
| } catch (Exception ex) { |
| // Apps should handle exceptions. But if they don't, report the error on |
| // behalf of them. |
| AppSearchResult failedResult = AppSearchResult.throwableToFailedResult(ex); |
| safeCallback.onResult(AppSearchResultParcel.fromFailedResult(failedResult)); |
| } |
| } |
| }; |
| |
| @NonNull |
| @Override |
| public final IBinder onBind(@Nullable Intent intent) { |
| return mBinder; |
| } |
| |
| /** |
| * 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". 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. |
| * |
| * @param request The function execution request. |
| * @param callback A callback to report back the result. |
| */ |
| @MainThread |
| public abstract void onExecuteFunction( |
| @NonNull ExecuteAppFunctionRequest request, |
| @NonNull Consumer<AppSearchResult<ExecuteAppFunctionResponse>> callback); |
| } |