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