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.appfunctions; 18 19 import static android.Manifest.permission.BIND_APP_FUNCTION_SERVICE; 20 import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER; 21 import static android.content.pm.PackageManager.PERMISSION_DENIED; 22 23 import android.annotation.FlaggedApi; 24 import android.annotation.MainThread; 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.SdkConstant; 28 import android.app.Service; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.pm.SigningInfo; 32 import android.os.Binder; 33 import android.os.CancellationSignal; 34 import android.os.IBinder; 35 import android.os.ICancellationSignal; 36 import android.os.OutcomeReceiver; 37 import android.os.RemoteException; 38 39 /** 40 * Abstract base class to provide app functions to the system. 41 * 42 * <p>Include the following in the manifest: 43 * 44 * <pre> 45 * {@literal 46 * <service android:name=".YourService" 47 * android:permission="android.permission.BIND_APP_FUNCTION_SERVICE"> 48 * <intent-filter> 49 * <action android:name="android.app.appfunctions.AppFunctionService" /> 50 * </intent-filter> 51 * </service> 52 * } 53 * </pre> 54 * 55 * @see AppFunctionManager 56 */ 57 @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) 58 public abstract class AppFunctionService extends Service { 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 BIND_APP_FUNCTION_SERVICE} permission so that other 62 * applications can not abuse it. 63 */ 64 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 65 @NonNull 66 public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService"; 67 68 /** 69 * Functional interface to represent the execution logic of an app function. 70 * 71 * @hide 72 */ 73 @FunctionalInterface 74 public interface OnExecuteFunction { 75 /** 76 * Performs the semantic of executing the function specified by the provided request and 77 * return the response through the provided callback. 78 */ perform( @onNull ExecuteAppFunctionRequest request, @NonNull String callingPackage, @NonNull SigningInfo callingPackageSigningInfo, @NonNull CancellationSignal cancellationSignal, @NonNull OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> callback)79 void perform( 80 @NonNull ExecuteAppFunctionRequest request, 81 @NonNull String callingPackage, 82 @NonNull SigningInfo callingPackageSigningInfo, 83 @NonNull CancellationSignal cancellationSignal, 84 @NonNull 85 OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> callback); 86 } 87 88 /** @hide */ 89 @NonNull createBinder( @onNull Context context, @NonNull OnExecuteFunction onExecuteFunction)90 public static Binder createBinder( 91 @NonNull Context context, @NonNull OnExecuteFunction onExecuteFunction) { 92 return new IAppFunctionService.Stub() { 93 @Override 94 public void executeAppFunction( 95 @NonNull ExecuteAppFunctionRequest request, 96 @NonNull String callingPackage, 97 @NonNull SigningInfo callingPackageSigningInfo, 98 @NonNull ICancellationCallback cancellationCallback, 99 @NonNull IExecuteAppFunctionCallback callback) { 100 if (context.checkCallingPermission(BIND_APP_FUNCTION_SERVICE) 101 == PERMISSION_DENIED) { 102 throw new SecurityException("Can only be called by the system server."); 103 } 104 SafeOneTimeExecuteAppFunctionCallback safeCallback = 105 new SafeOneTimeExecuteAppFunctionCallback(callback); 106 try { 107 onExecuteFunction.perform( 108 request, 109 callingPackage, 110 callingPackageSigningInfo, 111 buildCancellationSignal(cancellationCallback), 112 new OutcomeReceiver<>() { 113 @Override 114 public void onResult(ExecuteAppFunctionResponse result) { 115 safeCallback.onResult(result); 116 } 117 118 @Override 119 public void onError(AppFunctionException exception) { 120 safeCallback.onError(exception); 121 } 122 }); 123 } catch (Exception ex) { 124 // Apps should handle exceptions. But if they don't, report the error on 125 // behalf of them. 126 safeCallback.onError( 127 new AppFunctionException(toErrorCode(ex), ex.getMessage())); 128 } 129 } 130 }; 131 } 132 133 private static CancellationSignal buildCancellationSignal( 134 @NonNull ICancellationCallback cancellationCallback) { 135 final ICancellationSignal cancellationSignalTransport = 136 CancellationSignal.createTransport(); 137 CancellationSignal cancellationSignal = 138 CancellationSignal.fromTransport(cancellationSignalTransport); 139 try { 140 cancellationCallback.sendCancellationTransport(cancellationSignalTransport); 141 } catch (RemoteException e) { 142 throw e.rethrowFromSystemServer(); 143 } 144 145 return cancellationSignal; 146 } 147 148 private final Binder mBinder = 149 createBinder(AppFunctionService.this, AppFunctionService.this::onExecuteFunction); 150 151 @NonNull 152 @Override 153 public final IBinder onBind(@Nullable Intent intent) { 154 return mBinder; 155 } 156 157 /** 158 * Called by the system to execute a specific app function. 159 * 160 * <p>This method is the entry point for handling all app function requests in an app. When the 161 * system needs your AppFunctionService to perform a function, it will invoke this method. 162 * 163 * <p>Each function you've registered is identified by a unique identifier. This identifier 164 * doesn't need to be globally unique, but it must be unique within your app. For example, a 165 * function to order food could be identified as "orderFood". In most cases, this identifier is 166 * automatically generated by the AppFunctions SDK. 167 * 168 * <p>You can determine which function to execute by calling {@link 169 * ExecuteAppFunctionRequest#getFunctionIdentifier()}. This allows your service to route the 170 * incoming request to the appropriate logic for handling the specific function. 171 * 172 * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker 173 * thread and dispatch the result with the given callback. You should always report back the 174 * result using the callback, no matter if the execution was successful or not. 175 * 176 * <p>This method also accepts a {@link CancellationSignal} that the app should listen to cancel 177 * the execution of function if requested by the system. 178 * 179 * @param request The function execution request. 180 * @param callingPackage The package name of the app that is requesting the execution. 181 * @param callingPackageSigningInfo The signing information of the app that is requesting the 182 * execution. 183 * @param cancellationSignal A signal to cancel the execution. 184 * @param callback A callback to report back the result or error. 185 */ 186 @MainThread 187 public abstract void onExecuteFunction( 188 @NonNull ExecuteAppFunctionRequest request, 189 @NonNull String callingPackage, 190 @NonNull SigningInfo callingPackageSigningInfo, 191 @NonNull CancellationSignal cancellationSignal, 192 @NonNull OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> callback); 193 194 /** 195 * Returns result codes from throwable. 196 * 197 * @hide 198 */ 199 private static @AppFunctionException.ErrorCode int toErrorCode(@NonNull Throwable t) { 200 if (t instanceof IllegalArgumentException) { 201 return AppFunctionException.ERROR_INVALID_ARGUMENT; 202 } 203 return AppFunctionException.ERROR_APP_UNKNOWN_ERROR; 204 } 205 } 206