1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app.appsearch.functions; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.MainThread; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.app.Service; 24 import android.app.appsearch.AppSearchResult; 25 import android.app.appsearch.aidl.AppSearchResultParcel; 26 import android.app.appsearch.aidl.IAppFunctionService; 27 import android.app.appsearch.aidl.IAppSearchResultCallback; 28 import android.content.Intent; 29 import android.os.Binder; 30 import android.os.IBinder; 31 import android.os.Process; 32 33 import com.android.appsearch.flags.Flags; 34 35 import java.util.function.Consumer; 36 37 /** 38 * Abstract base class to provide app functions to the system. 39 * 40 * <p>Include the following in the manifest: 41 * 42 * <pre> 43 * {@literal 44 * <service android:name=".YourService" 45 * android:permission="android.permission.BIND_APP_FUNCTION_SERVICE"> 46 * <intent-filter> 47 * <action android:name="android.app.appsearch.functions.AppFunctionService" /> 48 * </intent-filter> 49 * </service> 50 * } 51 * </pre> 52 * 53 * @see AppFunctionManager 54 */ 55 @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS) 56 public abstract class AppFunctionService extends Service { 57 private static final String TAG = "AppSearchAppFunction"; 58 59 /** 60 * The {@link Intent} that must be declared as handled by the service. To be supported, the 61 * service must also require the {@link AppFunctionManager#PERMISSION_BIND_APP_FUNCTION_SERVICE} 62 * permission so that other applications can not abuse it. 63 */ 64 @NonNull 65 public static final String SERVICE_INTERFACE = 66 "android.app.appsearch.functions.AppFunctionService"; 67 68 private final Binder mBinder = 69 new IAppFunctionService.Stub() { 70 @Override 71 public void executeAppFunction( 72 @NonNull ExecuteAppFunctionRequest request, 73 @NonNull IAppSearchResultCallback callback) { 74 // TODO(b/327134039): Replace this check with the new permission 75 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 76 throw new SecurityException("Can only be called by the system server"); 77 } 78 SafeOneTimeAppSearchResultCallback safeCallback = 79 new SafeOneTimeAppSearchResultCallback(callback); 80 try { 81 AppFunctionService.this.onExecuteFunction( 82 request, 83 appFunctionResult -> { 84 AppSearchResultParcel appSearchResultParcel; 85 // Create result from value in success case and errorMessage in 86 // failure case. 87 if (appFunctionResult.isSuccess()) { 88 appSearchResultParcel = 89 AppSearchResultParcel 90 .fromExecuteAppFunctionResponse( 91 appFunctionResult.getResultValue()); 92 } else { 93 appSearchResultParcel = 94 AppSearchResultParcel.fromFailedResult( 95 appFunctionResult); 96 } 97 safeCallback.onResult(appSearchResultParcel); 98 }); 99 } catch (Exception ex) { 100 // Apps should handle exceptions. But if they don't, report the error on 101 // behalf of them. 102 AppSearchResult failedResult = AppSearchResult.throwableToFailedResult(ex); 103 safeCallback.onResult(AppSearchResultParcel.fromFailedResult(failedResult)); 104 } 105 } 106 }; 107 108 @NonNull 109 @Override onBind(@ullable Intent intent)110 public final IBinder onBind(@Nullable Intent intent) { 111 return mBinder; 112 } 113 114 /** 115 * Called by the system to execute a specific app function. 116 * 117 * <p>This method is triggered when the system requests your AppFunctionService to handle a 118 * particular function you have registered and made available. 119 * 120 * <p>To ensure proper routing of function requests, assign a unique identifier to each 121 * function. This identifier doesn't need to be globally unique, but it must be unique within 122 * your app. For example, a function to order food could be identified as "orderFood". You can 123 * determine the specific function to invoke by calling {@link 124 * ExecuteAppFunctionRequest#getFunctionIdentifier()}. 125 * 126 * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker 127 * thread and dispatch the result with the given callback. You should always report back the 128 * result using the callback, no matter if the execution was successful or not. 129 * 130 * @param request The function execution request. 131 * @param callback A callback to report back the result. 132 */ 133 @MainThread onExecuteFunction( @onNull ExecuteAppFunctionRequest request, @NonNull Consumer<AppSearchResult<ExecuteAppFunctionResponse>> callback)134 public abstract void onExecuteFunction( 135 @NonNull ExecuteAppFunctionRequest request, 136 @NonNull Consumer<AppSearchResult<ExecuteAppFunctionResponse>> callback); 137 } 138