1 /*
2  * Copyright 2025 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 androidx.appfunctions
18 
19 import android.os.Bundle
20 import androidx.annotation.IntDef
21 import androidx.annotation.RestrictTo
22 
23 /**
24  * An exception that is thrown when an error occurs during an app function execution.
25  *
26  * This exception can be used by the app to report errors to the caller.
27  */
28 public abstract class AppFunctionException
29 internal constructor(
30     /** The error code. */
31     @ErrorCode internal val internalErrorCode: Int,
32     /** The error message. */
33     public val errorMessage: String?,
34     internal val extras: Bundle
35 ) : Exception(errorMessage) {
36     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
toPlatformExtensionsClassnull37     public fun toPlatformExtensionsClass():
38         com.android.extensions.appfunctions.AppFunctionException {
39         return com.android.extensions.appfunctions.AppFunctionException(
40             internalErrorCode,
41             errorMessage,
42             extras
43         )
44     }
45 
46     /**
47      * Returns the error category.
48      *
49      * This method categorizes errors based on their underlying cause, allowing developers to
50      * implement targeted error handling and provide more informative error messages to users. It
51      * maps ranges of error codes to specific error categories.
52      *
53      * This method returns [ERROR_CATEGORY_UNKNOWN] if the error code does not belong to any error
54      * category.
55      */
56     @ErrorCategory
57     internal val errorCategory: Int =
58         when (internalErrorCode) {
59             in 1000..1999 -> ERROR_CATEGORY_REQUEST_ERROR
60             in 2000..2999 -> ERROR_CATEGORY_SYSTEM
61             in 3000..3999 -> ERROR_CATEGORY_APP
62             else -> ERROR_CATEGORY_UNKNOWN
63         }
64 
65     @IntDef(
66         value =
67             [
68                 ERROR_CATEGORY_UNKNOWN,
69                 ERROR_CATEGORY_REQUEST_ERROR,
70                 ERROR_CATEGORY_APP,
71                 ERROR_CATEGORY_SYSTEM,
72             ]
73     )
74     internal annotation class ErrorCategory
75 
76     @IntDef(
77         value =
78             [
79                 ERROR_DENIED,
80                 ERROR_INVALID_ARGUMENT,
81                 ERROR_DISABLED,
82                 ERROR_FUNCTION_NOT_FOUND,
83                 ERROR_RESOURCE_NOT_FOUND,
84                 ERROR_LIMIT_EXCEEDED,
85                 ERROR_RESOURCE_ALREADY_EXISTS,
86                 ERROR_SYSTEM_ERROR,
87                 ERROR_CANCELLED,
88                 ERROR_APP_UNKNOWN_ERROR,
89                 ERROR_PERMISSION_REQUIRED,
90                 ERROR_NOT_SUPPORTED,
91             ]
92     )
93     internal annotation class ErrorCode
94 
95     public companion object {
96         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
97         @SuppressWarnings("WrongConstant")
fromPlatformExtensionsClassnull98         public fun fromPlatformExtensionsClass(
99             exception: com.android.extensions.appfunctions.AppFunctionException
100         ): AppFunctionException {
101             return when (exception.errorCode) {
102                 ERROR_DENIED -> AppFunctionDeniedException(exception.errorMessage, exception.extras)
103                 ERROR_INVALID_ARGUMENT ->
104                     AppFunctionInvalidArgumentException(exception.errorMessage, exception.extras)
105                 ERROR_DISABLED ->
106                     AppFunctionDisabledException(exception.errorMessage, exception.extras)
107                 ERROR_FUNCTION_NOT_FOUND ->
108                     AppFunctionFunctionNotFoundException(exception.errorMessage, exception.extras)
109                 ERROR_RESOURCE_NOT_FOUND ->
110                     AppFunctionElementNotFoundException(exception.errorMessage, exception.extras)
111                 ERROR_LIMIT_EXCEEDED ->
112                     AppFunctionLimitExceededException(exception.errorMessage, exception.extras)
113                 ERROR_RESOURCE_ALREADY_EXISTS ->
114                     AppFunctionElementAlreadyExistsException(
115                         exception.errorMessage,
116                         exception.extras
117                     )
118                 ERROR_SYSTEM_ERROR ->
119                     AppFunctionSystemUnknownException(exception.errorMessage, exception.extras)
120                 ERROR_CANCELLED ->
121                     AppFunctionCancelledException(exception.errorMessage, exception.extras)
122                 ERROR_APP_UNKNOWN_ERROR ->
123                     AppFunctionAppUnknownException(exception.errorMessage, exception.extras)
124                 ERROR_PERMISSION_REQUIRED ->
125                     AppFunctionPermissionRequiredException(exception.errorMessage, exception.extras)
126                 ERROR_NOT_SUPPORTED ->
127                     AppFunctionNotSupportedException(exception.errorMessage, exception.extras)
128                 else ->
129                     AppFunctionUnknownException(
130                         exception.errorCode,
131                         exception.errorMessage,
132                         exception.extras,
133                     )
134             }
135         }
136 
137         // Error categories
138         /** The error category is unknown. */
139         internal const val ERROR_CATEGORY_UNKNOWN: Int = 0
140 
141         /**
142          * The error is caused by the app requesting a function execution.
143          *
144          * For example, the caller provided invalid parameters in the execution request e.g. an
145          * invalid function ID.
146          *
147          * Errors in the category fall in the range 1000-1999 inclusive.
148          */
149         internal const val ERROR_CATEGORY_REQUEST_ERROR: Int = 1
150 
151         /**
152          * The error is caused by an issue in the system.
153          *
154          * For example, the AppFunctionService implementation is not found by the system.
155          *
156          * Errors in the category fall in the range 2000-2999 inclusive.
157          */
158         internal const val ERROR_CATEGORY_SYSTEM: Int = 2
159 
160         /**
161          * The error is caused by the app providing the function.
162          *
163          * For example, the app crashed when the system is executing the request.
164          *
165          * Errors in the category fall in the range 3000-3999 inclusive.
166          */
167         internal const val ERROR_CATEGORY_APP: Int = 3
168 
169         // Error codes
170         /**
171          * The caller does not have the permission to execute an app function.
172          *
173          * This is different from [ERROR_PERMISSION_REQUIRED] in that the caller is missing this
174          * specific permission, as opposed to the target app missing a permission.
175          *
176          * This error is in the [ERROR_CATEGORY_REQUEST_ERROR] category.
177          */
178         internal const val ERROR_DENIED: Int = 1000
179 
180         /**
181          * The caller supplied invalid arguments to the execution request.
182          *
183          * This error may be considered similar to [IllegalArgumentException].
184          *
185          * This error is in the [ERROR_CATEGORY_REQUEST_ERROR] category.
186          */
187         internal const val ERROR_INVALID_ARGUMENT: Int = 1001
188 
189         /**
190          * The caller tried to execute a disabled app function.
191          *
192          * This error is in the [ERROR_CATEGORY_REQUEST_ERROR] category.
193          */
194         internal const val ERROR_DISABLED: Int = 1002
195 
196         /**
197          * The caller tried to execute a function that does not exist.
198          *
199          * This error is in the [ERROR_CATEGORY_REQUEST_ERROR] category.
200          */
201         internal const val ERROR_FUNCTION_NOT_FOUND: Int = 1003
202 
203         // SDK-defined error codes in the [ERROR_CATEGORY_REQUEST_ERROR] category start from 1500.
204         /**
205          * The caller tried to request a resource/entity that does not exist.
206          *
207          * This error is in the [ERROR_CATEGORY_REQUEST_ERROR] category.
208          */
209         internal const val ERROR_RESOURCE_NOT_FOUND: Int = 1500
210 
211         /**
212          * The caller exceeded the allowed request rate.
213          *
214          * This error is in the [ERROR_CATEGORY_REQUEST_ERROR] category.
215          */
216         internal const val ERROR_LIMIT_EXCEEDED: Int = 1501
217 
218         /**
219          * The caller tried to create a resource/entity that already exists or has conflicts with
220          * existing resource/entity.
221          *
222          * This error is in the [ERROR_CATEGORY_REQUEST_ERROR] category.
223          */
224         internal const val ERROR_RESOURCE_ALREADY_EXISTS: Int = 1502
225 
226         /**
227          * An internal unexpected error coming from the system.
228          *
229          * This error is in the [ERROR_CATEGORY_SYSTEM] category.
230          */
231         internal const val ERROR_SYSTEM_ERROR: Int = 2000
232 
233         /**
234          * The operation was cancelled. Use this error code to report that a cancellation is done
235          * after receiving a cancellation signal.
236          *
237          * This error is in the [ERROR_CATEGORY_SYSTEM] category.
238          */
239         internal const val ERROR_CANCELLED: Int = 2001
240 
241         /**
242          * An unknown error occurred while processing the call in the AppFunctionService.
243          *
244          * This error is thrown when the service is connected in the remote application but an
245          * unexpected error is thrown from the bound application.
246          *
247          * This error is in the [ERROR_CATEGORY_APP] category.
248          */
249         internal const val ERROR_APP_UNKNOWN_ERROR: Int = 3000
250 
251         // SDK-defined error codes in the [ERROR_CATEGORY_APP] category start from 3500.
252         /**
253          * Indicates the app lacks the necessary permission to fulfill the request.
254          *
255          * This occurs when the app attempts an operation requiring user-granted permission that has
256          * not been provided. For example, creating a calendar event requires access to the calendar
257          * content. If the user hasn't granted this permission, this error should be thrown.
258          *
259          * This is different from [ERROR_DENIED] in that the required permission is missing from the
260          * target app, as opposed to the caller.
261          *
262          * This error is in the [ERROR_CATEGORY_APP] category.
263          */
264         internal const val ERROR_PERMISSION_REQUIRED: Int = 3500
265 
266         /**
267          * Indicates the action is not supported by the app.
268          *
269          * This error occurs when an app receives a request to perform an unsupported action. For
270          * example, a clock app might support updating timer properties such as label but may not
271          * allow updating the timer's duration once the timer has already started.
272          *
273          * This error is in the [ERROR_CATEGORY_APP] category.
274          */
275         internal const val ERROR_NOT_SUPPORTED: Int = 3501
276     }
277 }
278 
279 /**
280  * Thrown when an unknown error has occurred.
281  *
282  * This Exception is used when the error doesn't belong to any other AppFunctionException. This may
283  * happen due to version skews in the error codes between the platform and the sdk. E.g. if the app
284  * is running on a newer platform version (with a new error code) and an older sdk.
285  *
286  * Note that this is different from [AppFunctionAppUnknownException], in that the error wasn't
287  * necessarily caused by the app.
288  */
289 public class AppFunctionUnknownException
290 internal constructor(public val errorCode: Int, errorMessage: String? = null, extras: Bundle) :
291     AppFunctionException(errorCode, errorMessage, extras) {
292     /**
293      * Create an [AppFunctionUnknownException].
294      *
295      * @param errorCode The error code.
296      * @param errorMessage The error message.
297      */
298     public constructor(
299         errorCode: Int,
300         errorMessage: String? = null
301     ) : this(errorCode, errorMessage, Bundle.EMPTY)
302 }
303