1 /*
<lambda>null2  * 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.app.PendingIntent
20 import android.app.appsearch.GenericDocument
21 import android.net.Uri
22 import android.os.Build
23 import android.os.Bundle
24 import android.util.Log
25 import androidx.annotation.RequiresApi
26 import androidx.annotation.RestrictTo
27 import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
28 import androidx.annotation.VisibleForTesting
29 import androidx.appfunctions.internal.AppFunctionSerializableFactory
30 import androidx.appfunctions.internal.Constants.APP_FUNCTIONS_TAG
31 import androidx.appfunctions.metadata.AppFunctionComponentsMetadata
32 import androidx.appfunctions.metadata.AppFunctionObjectTypeMetadata
33 import androidx.appfunctions.metadata.AppFunctionParameterMetadata
34 import java.time.LocalDateTime
35 
36 /**
37  * A data class to contain information to be communicated between AppFunctions apps and agents.
38  *
39  * This class can be logically viewed as a mapping from [String] keys to properties of various
40  * supported types, or arrays of supported types.
41  *
42  * When trying to retrieve an associated value, [AppFunctionData] would validate the request against
43  * the predefined metadata specification provided from [Builder].
44  *
45  * For example,
46  * ```
47  * fun callCreateNoteFunction(
48  *   metadata: AppFunctionMetadata,
49  *   request: ExecuteAppFunctionRequest,
50  * ) {
51  *   val response = appFunctionManager.executeAppFunction(request)
52  *
53  *   if (metadata.response.valueType is AppFunctionObjectTypeMetadata) {
54  *     val returnData = response.returnValue.getAppFunctionData(
55  *       ExecuteAppFunctionResponse.Success.PROPERTY_RETURN_VALUE
56  *     )
57  *     val title = returnData.getString("title")
58  *     // Throws an error if "owner" doesn't exist according to the metadata
59  *     val owner = returnData.getString("owner")
60  *     // Throws an error if "content" is String.
61  *     val content = returnData.getInt("content")
62  *   }
63  * }
64  * ```
65  */
66 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
67 public class AppFunctionData
68 internal constructor(
69     // TODO: Make it non-null once the constructor that takes qualifiedName has removed
70     internal val spec: AppFunctionDataSpec?,
71     @get:VisibleForTesting
72     @get:RestrictTo(LIBRARY_GROUP)
73     public val genericDocument: GenericDocument,
74     internal val extras: Bundle
75 ) {
76 
77     // TODO: Remove this constructor
78     @RestrictTo(LIBRARY_GROUP)
79     public constructor(
80         genericDocument: GenericDocument,
81         extras: Bundle,
82     ) : this(null, genericDocument, extras)
83 
84     /** Qualified name of the underlying object */
85     public val qualifiedName: String
86         get() = genericDocument.schemaType
87 
88     /**
89      * Returns the ID of the underlying [GenericDocument]. Only use this for handling legacy schema.
90      */
91     @get:RestrictTo(LIBRARY_GROUP) public val id: String = genericDocument.id
92 
93     /**
94      * Checks if [AppFunctionData] has an associated value with the specified [key].
95      *
96      * @param key The key to checks for.
97      * @return True if there is an associated value. Otherwise, false.
98      * @throws IllegalArgumentException If there is no metadata related to [key].
99      */
100     public fun containsKey(key: String): Boolean {
101         if (spec != null && !spec.containsMetadata(key)) {
102             throw IllegalArgumentException("There is no metadata associated with $key")
103         }
104         return genericDocument.getProperty(key) != null || extras.containsKey(key)
105     }
106 
107     /**
108      * Retrieves a [Boolean] value associated with the specified [key].
109      *
110      * @param key The key to retrieve the value for.
111      * @return The value associated with the [key]. It would return a default value false if the
112      *   associated value is not found.
113      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
114      *   according to the metadata specification.
115      */
116     public fun getBoolean(key: String): Boolean {
117         return getBoolean(key, DEFAULT_BOOLEAN)
118     }
119 
120     /**
121      * Retrieves a [Boolean] value associated with the specified [key], or returns [defaultValue] if
122      * the associated value is not found.
123      *
124      * @param key The key to retrieve the value for.
125      * @param defaultValue The default value if the associated value is not found.
126      * @return The value associated with the [key], or the [defaultValue] if the associated value is
127      *   not required and it is not found.
128      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
129      *   according to the metadata specification.
130      */
131     public fun getBoolean(key: String, defaultValue: Boolean): Boolean {
132         return getBooleanOrNull(key) ?: defaultValue
133     }
134 
135     /**
136      * Retrieves a [Boolean] value associated with the specified [key].
137      *
138      * @param key The key to retrieve the value for.
139      * @return The value associated with the [key], or null if the associated value is not found.
140      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
141      *   according to the metadata specification.
142      */
143     @RestrictTo(LIBRARY_GROUP)
144     public fun getBooleanOrNull(key: String): Boolean? {
145         val array = unsafeGetProperty(key, BooleanArray::class.java)
146         val booleanValue =
147             if (array == null || array.isEmpty()) {
148                 null
149             } else {
150                 array[0]
151             }
152         spec?.validateReadRequest(
153             key,
154             Boolean::class.java,
155             isCollection = false,
156         )
157         return booleanValue
158     }
159 
160     /**
161      * Retrieves a [Float] value associated with the specified [key].
162      *
163      * @param key The key to retrieve the value for.
164      * @return The value associated with the [key]. It would return a default value 0.0 if the
165      *   associated value is not found.
166      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
167      *   according to the metadata specification.
168      */
169     public fun getFloat(key: String): Float {
170         return getFloat(key, DEFAULT_FLOAT)
171     }
172 
173     /**
174      * Retrieves a [Float] value associated with the specified [key], or returns [defaultValue] if
175      * the associated value is not found.
176      *
177      * @param key The key to retrieve the value for.
178      * @param defaultValue The default value if the associated value is not found.
179      * @return The value associated with the [key], or the [defaultValue] if the associated is not
180      *   found.
181      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
182      *   according to the metadata specification.
183      */
184     public fun getFloat(key: String, defaultValue: Float): Float {
185         return getFloatOrNull(key) ?: defaultValue
186     }
187 
188     /**
189      * Retrieves a [Float] value associated with the specified [key].
190      *
191      * @param key The key to retrieve the value for.
192      * @return The value associated with the [key], or null if the associated value is not found.
193      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
194      *   according to the metadata specification.
195      */
196     @RestrictTo(LIBRARY_GROUP)
197     public fun getFloatOrNull(key: String): Float? {
198         val array = unsafeGetProperty(key, DoubleArray::class.java)
199         val doubleValue =
200             if (array == null || array.isEmpty()) {
201                 null
202             } else {
203                 array[0]
204             }
205         spec?.validateReadRequest(
206             key,
207             Float::class.java,
208             isCollection = false,
209         )
210         if (doubleValue != null && !isDoubleWithinFloatRange(doubleValue)) {
211             // This should never happen because the setters forbid such value to exist in the
212             // first place.
213             throw IllegalStateException(
214                 "The value associated with $key is not within the range of Float"
215             )
216         }
217         return doubleValue?.toFloat()
218     }
219 
220     /**
221      * Retrieves a [Double] value associated with the specified [key].
222      *
223      * @param key The key to retrieve the value for.
224      * @return The value associated with the [key]. It would return a default value 0.0 if the
225      *   associated value is not found.
226      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
227      *   according to the metadata specification.
228      */
229     public fun getDouble(key: String): Double {
230         return getDouble(key, DEFAULT_DOUBLE)
231     }
232 
233     /**
234      * Retrieves a [Double] value associated with the specified [key], or returns [defaultValue] if
235      * the associated value is not found.
236      *
237      * @param key The key to retrieve the value for.
238      * @param defaultValue The default value if the associated value is not found.
239      * @return The value associated with the [key], or the [defaultValue] if the associated value is
240      *   not found.
241      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
242      *   according to the metadata specification.
243      */
244     public fun getDouble(key: String, defaultValue: Double): Double {
245         return getDoubleOrNull(key) ?: defaultValue
246     }
247 
248     /**
249      * Retrieves a [Double] value associated with the specified [key].
250      *
251      * @param key The key to retrieve the value for.
252      * @return The value associated with the [key], or null if the associated value is not found.
253      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
254      *   according to the metadata specification.
255      */
256     @RestrictTo(LIBRARY_GROUP)
257     public fun getDoubleOrNull(key: String): Double? {
258         val array = unsafeGetProperty(key, DoubleArray::class.java)
259         val doubleValue =
260             if (array == null || array.isEmpty()) {
261                 null
262             } else {
263                 array[0]
264             }
265         spec?.validateReadRequest(
266             key,
267             Double::class.java,
268             isCollection = false,
269         )
270         return doubleValue
271     }
272 
273     /**
274      * Retrieves an [Int] value associated with the specified [key].
275      *
276      * @param key The key to retrieve the value for.
277      * @return The value associated with the [key]. It would return a default value 0L if the
278      *   associated value is not found.
279      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
280      *   according to the metadata specification.
281      */
282     public fun getInt(key: String): Int {
283         return getInt(key, DEFAULT_INT)
284     }
285 
286     /**
287      * Retrieves an [Int] value associated with the specified [key], or returns [defaultValue] if
288      * the associated value is not found.
289      *
290      * @param key The key to retrieve the value for.
291      * @param defaultValue The default value if the associated value is not found.
292      * @return The value associated with the [key], or the [defaultValue] if the associated value is
293      *   not found.
294      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
295      *   according to the metadata specification.
296      */
297     public fun getInt(key: String, defaultValue: Int): Int {
298         return getIntOrNull(key) ?: defaultValue
299     }
300 
301     /**
302      * Retrieves an [Int] value associated with the specified [key].
303      *
304      * @param key The key to retrieve the value for.
305      * @return The value associated with the [key], or null if the associated value is not found.
306      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
307      *   according to the metadata specification.
308      */
309     @RestrictTo(LIBRARY_GROUP)
310     public fun getIntOrNull(key: String): Int? {
311         val array = unsafeGetProperty(key, LongArray::class.java)
312         val longValue =
313             if (array == null || array.isEmpty()) {
314                 null
315             } else {
316                 array[0]
317             }
318         spec?.validateReadRequest(
319             key,
320             Int::class.java,
321             isCollection = false,
322         )
323         if (longValue != null && !isLongWithinLongRange(longValue)) {
324             // This should never happen because the setters forbid such value to exist in the
325             // first place.
326             throw IllegalStateException(
327                 "The value associated with $key is not within the range of Int"
328             )
329         }
330         return longValue?.toInt()
331     }
332 
333     /**
334      * Retrieves a [Long] value associated with the specified [key].
335      *
336      * @param key The key to retrieve the value for.
337      * @return The value associated with the [key]. It would return a default value 0L if the
338      *   associated value is not found.
339      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
340      *   according to the metadata specification.
341      */
342     public fun getLong(key: String): Long {
343         return getLong(key, DEFAULT_LONG)
344     }
345 
346     /**
347      * Retrieves a [Long] value associated with the specified [key], or returns [defaultValue] if
348      * the associated value is not found.
349      *
350      * @param key The key to retrieve the value for.
351      * @param defaultValue The default value if the associated value is not found.
352      * @return The value associated with the [key], or the [defaultValue] if the associated value is
353      *   not found.
354      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
355      *   according to the metadata specification.
356      */
357     public fun getLong(key: String, defaultValue: Long): Long {
358         return getLongOrNull(key) ?: defaultValue
359     }
360 
361     /**
362      * Retrieves a [Long] value associated with the specified [key].
363      *
364      * @param key The key to retrieve the value for.
365      * @return The value associated with the [key], or null if the associated value is not found.
366      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
367      *   according to the metadata specification.
368      */
369     @RestrictTo(LIBRARY_GROUP)
370     public fun getLongOrNull(key: String): Long? {
371         val array = unsafeGetProperty(key, LongArray::class.java)
372         val longValue =
373             if (array == null || array.isEmpty()) {
374                 null
375             } else {
376                 array[0]
377             }
378         spec?.validateReadRequest(
379             key,
380             Long::class.java,
381             isCollection = false,
382         )
383         return longValue
384     }
385 
386     /**
387      * Retrieves a [String] value associated with the specified [key].
388      *
389      * @param key The key to retrieve the value for.
390      * @return The value associated with the [key], or null if the associated value is not found.
391      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
392      *   according to the metadata specification.
393      */
394     public fun getString(key: String): String? {
395         return getStringOrNull(key)
396     }
397 
398     /**
399      * Retrieves a [String] value associated with the specified [key], or returns null if the
400      * associated value is not found.
401      *
402      * This method is used internally by the [AppFunctionSerializableFactory] to retrieve the
403      * underlying string value.
404      *
405      * @param key The key to retrieve the value for.
406      * @return The value associated with the [key], or null if the associated value is not found.
407      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
408      *   according to the metadata specification.
409      */
410     @RestrictTo(LIBRARY_GROUP)
411     public fun getStringOrNull(key: String): String? {
412         val array = unsafeGetProperty(key, Array<String>::class.java)
413         val stringValue =
414             if (array == null || array.isEmpty()) {
415                 null
416             } else {
417                 array[0]
418             }
419         spec?.validateReadRequest(
420             key,
421             String::class.java,
422             isCollection = false,
423         )
424         return stringValue
425     }
426 
427     /**
428      * Retrieves an [AppFunctionData] value associated with the specified [key].
429      *
430      * @param key The key to retrieve the value for.
431      * @return The value associated with the [key], or null if the associated value is not found.
432      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
433      *   according to the metadata specification.
434      */
435     public fun getAppFunctionData(
436         key: String,
437     ): AppFunctionData? {
438         val array = unsafeGetProperty(key, Array<GenericDocument>::class.java)
439         val dataValue =
440             if (array == null || array.isEmpty()) {
441                 null
442             } else {
443                 AppFunctionData(
444                     spec?.getPropertyObjectSpec(key),
445                     array[0],
446                     extras.getBundle(extrasKey(key)) ?: Bundle.EMPTY
447                 )
448             }
449         spec?.validateReadRequest(
450             key,
451             AppFunctionData::class.java,
452             isCollection = false,
453         )
454         return dataValue
455     }
456 
457     /**
458      * Retrieves a [PendingIntent] value associated with the specified [key].
459      *
460      * @param key The key to retrieve the value for.
461      * @return The value associated with the [key], or null if the associated value is not found.
462      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
463      *   according to the metadata specification.
464      */
465     public fun getPendingIntent(key: String): PendingIntent? {
466         return getPendingIntentOrNull(key)
467     }
468 
469     /**
470      * Retrieves a [PendingIntent] value associated with the specified [key], or returns null if the
471      * associated value is not found.
472      *
473      * This method is used internally by the [AppFunctionSerializableFactory] to retrieve the
474      * underlying PendingIntent value.
475      *
476      * @param key The key to retrieve the value for.
477      * @return The value associated with the [key], or null if the associated value is not found.
478      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
479      *   according to the metadata specification.
480      */
481     @RestrictTo(LIBRARY_GROUP)
482     public fun getPendingIntentOrNull(key: String): PendingIntent? {
483         spec?.validateReadRequest(key, PendingIntent::class.java, isCollection = false)
484         return extras.getParcelable(key, PendingIntent::class.java)
485     }
486 
487     /**
488      * Retrieves a [BooleanArray] value associated with the specified [key].
489      *
490      * @param key The key to retrieve the value for.
491      * @return The value associated with the [key]. Or null if the associated value is not found.
492      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
493      *   according to the metadata specification.
494      */
495     public fun getBooleanArray(key: String): BooleanArray? {
496         val booleanArrayValue = unsafeGetProperty(key, BooleanArray::class.java)
497         spec?.validateReadRequest(
498             key,
499             Boolean::class.java,
500             isCollection = true,
501         )
502         return booleanArrayValue
503     }
504 
505     /**
506      * Retrieves a [FloatArray] value associated with the specified [key].
507      *
508      * @param key The key to retrieve the value for.
509      * @return The value associated with the [key]. Or null if the associated value is not found.
510      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
511      *   according to the metadata specification.
512      */
513     public fun getFloatArray(key: String): FloatArray? {
514         val doubleArrayValue = unsafeGetProperty(key, DoubleArray::class.java)
515         spec?.validateReadRequest(
516             key,
517             Float::class.java,
518             isCollection = true,
519         )
520         return doubleArrayValue
521             ?.map { doubleValue ->
522                 if (!isDoubleWithinFloatRange(doubleValue)) {
523                     // This should never happen because the setters forbid such value to exist in
524                     // the first place.
525                     throw IllegalStateException(
526                         "One of the value associated with $key is not within the range of Float"
527                     )
528                 }
529                 doubleValue.toFloat()
530             }
531             ?.toFloatArray()
532     }
533 
534     /**
535      * Retrieves a [DoubleArray] value associated with the specified [key].
536      *
537      * @param key The key to retrieve the value for.
538      * @return The value associated with the [key]. Or null if the associated value is not found.
539      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
540      *   according to the metadata specification.
541      */
542     public fun getDoubleArray(key: String): DoubleArray? {
543         val doubleArrayValue = unsafeGetProperty(key, DoubleArray::class.java)
544         spec?.validateReadRequest(
545             key,
546             Double::class.java,
547             isCollection = true,
548         )
549         return doubleArrayValue
550     }
551 
552     /**
553      * Retrieves an [IntArray] value associated with the specified [key].
554      *
555      * @param key The key to retrieve the value for.
556      * @return The value associated with the [key]. Or null if the associated value is not found.
557      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
558      *   according to the metadata specification.
559      */
560     public fun getIntArray(key: String): IntArray? {
561         val longArrayValue = unsafeGetProperty(key, LongArray::class.java)
562         spec?.validateReadRequest(
563             key,
564             Int::class.java,
565             isCollection = true,
566         )
567         return longArrayValue
568             ?.map { longValue ->
569                 if (!isLongWithinLongRange(longValue)) {
570                     // This should never happen because the setters forbid such value to exist in
571                     // the first place.
572                     throw IllegalStateException(
573                         "One of the value associated with $key is not within the range of Int"
574                     )
575                 }
576                 longValue.toInt()
577             }
578             ?.toIntArray()
579     }
580 
581     /**
582      * Retrieves a [LongArray] value associated with the specified [key].
583      *
584      * @param key The key to retrieve the value for.
585      * @return The value associated with the [key]. Or null if the associated value is not found.
586      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
587      *   according to the metadata specification.
588      */
589     public fun getLongArray(key: String): LongArray? {
590         val longArrayValue = unsafeGetProperty(key, LongArray::class.java)
591         spec?.validateReadRequest(
592             key,
593             Long::class.java,
594             isCollection = true,
595         )
596         return longArrayValue
597     }
598 
599     /**
600      * Retrieves a [ByteArray] value associated with the specified [key].
601      *
602      * @param key The key to retrieve the value for.
603      * @return The value associated with the [key]. Or null if the associated value is not found.
604      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
605      *   according to the metadata specification.
606      */
607     public fun getByteArray(key: String): ByteArray? {
608         val byteArrayValue = unsafeGetProperty(key, Array<ByteArray>::class.java)
609         spec?.validateReadRequest(
610             key,
611             Byte::class.java,
612             isCollection = true,
613         )
614         return if (byteArrayValue == null || byteArrayValue.isEmpty()) {
615             null
616         } else {
617             byteArrayValue[0]
618         }
619     }
620 
621     /**
622      * Retrieves a [List] of [String] value associated with the specified [key].
623      *
624      * @param key The key to retrieve the value for.
625      * @return The value associated with the [key]. Or null if the associated value is not found.
626      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
627      *   according to the metadata specification.
628      */
629     @Suppress("NullableCollection")
630     public fun getStringList(key: String): List<String>? {
631         val stringArrayValue = unsafeGetProperty(key, Array<String>::class.java)
632         spec?.validateReadRequest(
633             key,
634             String::class.java,
635             isCollection = true,
636         )
637         return stringArrayValue?.asList()
638     }
639 
640     /**
641      * Retrieves a [List] of [AppFunctionData] value associated with the specified [key].
642      *
643      * @param key The key to retrieve the value for.
644      * @return The value associated with the [key]. Or null if the associated value is not found.
645      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
646      *   according to the metadata specification.
647      */
648     @Suppress("NullableCollection")
649     public fun getAppFunctionDataList(
650         key: String,
651     ): List<AppFunctionData>? {
652         val propertySpec = spec?.getPropertyObjectSpec(key)
653         val dataArrayValue =
654             unsafeGetProperty(key, Array<GenericDocument>::class.java)?.mapIndexed { index, element
655                 ->
656                 AppFunctionData(
657                     propertySpec,
658                     element,
659                     extras.getBundle(extrasKey(key, index)) ?: Bundle.EMPTY
660                 )
661             }
662         spec?.validateReadRequest(
663             key,
664             AppFunctionData::class.java,
665             isCollection = true,
666         )
667         return dataArrayValue
668     }
669 
670     /**
671      * Retrieves a [List] of [PendingIntent] value associated with the specified [key].
672      *
673      * @param key The key to retrieve the value for.
674      * @return The value associated with the [key]. Or null if the associated value is not found.
675      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
676      *   according to the metadata specification.
677      */
678     @Suppress("NullableCollection")
679     public fun getPendingIntentList(key: String): List<PendingIntent>? {
680         spec?.validateReadRequest(key, PendingIntent::class.java, isCollection = true)
681         return extras.getParcelableArrayList(key, PendingIntent::class.java)
682     }
683 
684     /**
685      * Retrieves a generic value of type [T] associated with the specified [key].
686      *
687      * @param T The type of the associated value.
688      * @param key The key to retrieve the value for.
689      * @param valueClass The class of the type [T].
690      * @return The value associated with the [key]. Or null if the associated value is not found.
691      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
692      *   according to the metadata specification.
693      */
694     @RestrictTo(LIBRARY_GROUP)
695     public fun <T> getGenericField(key: String, valueClass: Class<T>): T {
696         @Suppress("UNCHECKED_CAST")
697         return when (valueClass) {
698             Int::class.java -> getIntOrNull(key) as T
699             Long::class.java -> getLongOrNull(key) as T
700             Float::class.java -> getFloatOrNull(key) as T
701             Double::class.java -> getDoubleOrNull(key) as T
702             Boolean::class.java -> getBooleanOrNull(key) as T
703             String::class.java -> getString(key) as T
704             PendingIntent::class.java -> getPendingIntent(key) as T
705             IntArray::class.java -> getIntArray(key) as T
706             LongArray::class.java -> getLongArray(key) as T
707             FloatArray::class.java -> getFloatArray(key) as T
708             DoubleArray::class.java -> getDoubleArray(key) as T
709             BooleanArray::class.java -> getBooleanArray(key) as T
710             ByteArray::class.java -> getByteArray(key) as T
711             // TODO(b/408385427): Handle deserialization in generic factory
712             else -> getAppFunctionData(key)?.deserialize(valueClass as Class<Any>) as T
713         }
714     }
715 
716     /**
717      * Retrieves a [List] of generic value of type [T] associated with the specified [key].
718      *
719      * @param I The [T]'s item type.
720      * @param T The type of the associated value.
721      * @param key The key to retrieve the value for.
722      * @param itemValueClass The class of the type [T].
723      * @return The list value associated with the [key]. Or null if the associated value is not
724      *   found.
725      * @throws IllegalArgumentException if the [key] is not allowed or the value type is incorrect
726      *   according to the metadata specification.
727      */
728     @RestrictTo(LIBRARY_GROUP)
729     public fun <I, T : List<I>?> getGenericListField(key: String, itemValueClass: Class<I>): T {
730         @Suppress("UNCHECKED_CAST")
731         return when (itemValueClass) {
732             String::class.java -> getStringList(key) as T
733             PendingIntent::class.java -> getPendingIntentList(key) as T
734             // TODO(b/408385427): Handle deserialization in generic factory
735             else ->
736                 getAppFunctionDataList(key)?.map { it.deserialize(itemValueClass as Class<Any>) }
737                     as T
738         }
739     }
740 
741     override fun toString(): String {
742         // TODO(b/391419368): Improve output to avoid reference to underlying GenericDocument
743         return "AppFunctionData(genericDocument=$genericDocument, extras=$extras)"
744     }
745 
746     private fun isDoubleWithinFloatRange(doubleValue: Double): Boolean {
747         if (doubleValue.isInfinite() || doubleValue.isNaN()) {
748             // Float also has NaN and Infinity representation
749             return true
750         }
751         if (doubleValue > Float.MAX_VALUE || doubleValue < -Float.MAX_VALUE) {
752             // The double value is not within the range of a Float.
753             return false
754         }
755         return true
756     }
757 
758     private fun isLongWithinLongRange(longValue: Long): Boolean {
759         return longValue >= Int.MIN_VALUE && longValue <= Int.MAX_VALUE
760     }
761 
762     /**
763      * Deserializes the [AppFunctionData] to an [AppFunctionSerializable] instance.
764      *
765      * @param serializableClass The AppFunctionSerializable class.
766      * @return The instance of [serializableClass].
767      * @throws IllegalArgumentException If unable to deserialize the [AppFunctionData] to an
768      *   instance of [serializableClass].
769      * @see [AppFunctionSerializable]
770      */
771     public fun <T : Any> deserialize(serializableClass: Class<T>): T {
772         return try {
773             val factory = getSerializableFactory(serializableClass)
774             factory.fromAppFunctionData(this)
775         } catch (e: Exception) {
776             Log.d(
777                 APP_FUNCTIONS_TAG,
778                 "Something went wrong while deserialize $this to $serializableClass",
779                 e
780             )
781             throw IllegalArgumentException(
782                 "Unable to deserialize $serializableClass. Is the class annotated with @AppFunctionSerializable?"
783             )
784         }
785     }
786 
787     /**
788      * Deserializes the [AppFunctionData] to an [AppFunctionSerializable] instance identified by
789      * [qualifiedName].
790      *
791      * @param qualifiedName The qualifiedName of the AppFunctionSerializable class.
792      * @return The instance of the class identified by [qualifiedName].
793      * @throws IllegalArgumentException If unable to deserialize the [AppFunctionData] to an
794      *   instance of the class identified by [qualifiedName].
795      * @see [AppFunctionSerializable]
796      */
797     @RestrictTo(LIBRARY_GROUP)
798     public fun <T : Any> deserialize(qualifiedName: String): T {
799         return deserialize<T>(getSerializableClass(qualifiedName))
800     }
801 
802     private fun <T : Any> unsafeGetProperty(key: String, arrayClass: Class<T>): T? {
803         return try {
804             val value = genericDocument.getProperty(key)
805             if (value != null) {
806                 arrayClass.cast(value)
807             } else {
808                 null
809             }
810         } catch (e: ClassCastException) {
811             throw IllegalArgumentException(
812                 "Found the property under [$key] but data type does not match with the request.",
813                 e,
814             )
815         }
816     }
817 
818     /**
819      * Builder for constructing [AppFunctionData]
820      *
821      * For example, to write an [AppFunctionData] for calling an AppFunction
822      *
823      * ```
824      * fun callCreateNoteFunction(metadata: AppFunctionMetadata) {
825      *   val appFunctionData = AppFunctionData.Builder(
826      *     metadata.parameters,
827      *     metadata.components,
828      *   ).apply {
829      *     setString("title", "Note Title")
830      *     // If the function doesn't accept "owner" as parameter, it would throw an error
831      *     setString("owner", "Me")
832      *     // If the function actually expects "content" as String, it would throw an error
833      *     setInt("content", 100)
834      *   }
835      *    .build()
836      * }
837      * ```
838      */
839     public class Builder {
840 
841         // TODO(b/399823985): Remove this once the constructor that takes qualifiedName has removed
842         private val qualifiedName: String
843         // TODO(b/399823985): Make it non-null once the constructor that takes qualifiedName has
844         // removed
845         private val spec: AppFunctionDataSpec?
846         private var genericDocumentBuilder: GenericDocument.Builder<*>
847         private val extrasBuilder = Bundle()
848 
849         // TODO(b/399823985): Clean up the usage without providing metadata.
850         /**
851          * @param id: Only set this when creating a document for the legacy schema. In the legacy
852          *   schema, ID is stored as [GenericDocument.id]. In Jetpack, ID is just a normal property.
853          */
854         @RestrictTo(LIBRARY_GROUP)
855         public constructor(qualifiedName: String, id: String = "") {
856             this.qualifiedName = qualifiedName
857             spec = null
858             genericDocumentBuilder =
859                 GenericDocument.Builder<GenericDocument.Builder<*>>("", id, qualifiedName)
860         }
861 
862         /**
863          * Constructs a [Builder] to create input data for an AppFunction execution call.
864          *
865          * This constructor is used when you need to write data that will be passed as input when
866          * executing an AppFunction. The [parameterMetadataList] defines the expected input
867          * parameters for that function.
868          *
869          * @param parameterMetadataList List of [AppFunctionParameterMetadata] defining the input
870          *   parameters.
871          * @param componentMetadata [AppFunctionComponentsMetadata] that has the shared data type.
872          */
873         public constructor(
874             parameterMetadataList: List<AppFunctionParameterMetadata>,
875             componentMetadata: AppFunctionComponentsMetadata,
876         ) : this(AppFunctionDataSpec.create(parameterMetadataList, componentMetadata))
877 
878         /**
879          * Constructs a [Builder] to create [AppFunctionData] representing an object.
880          *
881          * This constructor is used when you need to create [AppFunctionData] that represents an
882          * object used as either function parameters or return values, as defined by an
883          * [AppFunctionObjectTypeMetadata]. This metadata specifies the properties and their types
884          * for the object.
885          *
886          * @param objectTypeMetadata [AppFunctionObjectTypeMetadata] defining the object structure.
887          * @param componentMetadata [AppFunctionComponentsMetadata] that has the shared data type.
888          */
889         public constructor(
890             objectTypeMetadata: AppFunctionObjectTypeMetadata,
891             componentMetadata: AppFunctionComponentsMetadata,
892         ) : this(AppFunctionDataSpec.create(objectTypeMetadata, componentMetadata))
893 
894         private constructor(spec: AppFunctionDataSpec) {
895             this.spec = spec
896             this.qualifiedName = spec.objectQualifiedName
897             genericDocumentBuilder =
898                 GenericDocument.Builder<GenericDocument.Builder<*>>(
899                     "",
900                     "",
901                     spec.objectQualifiedName
902                 )
903         }
904 
905         /**
906          * Sets a [Boolean] value for the given [key].
907          *
908          * @param key The key to set the [Boolean] value for.
909          * @param value The [Boolean] value to set.
910          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
911          *   match the metadata specification associated with the [key].
912          */
913         public fun setBoolean(key: String, value: Boolean): Builder {
914             spec?.validateWriteRequest(key, Boolean::class.java, isCollection = false)
915             genericDocumentBuilder.setPropertyBoolean(key, value)
916             return this
917         }
918 
919         /**
920          * Sets a [Float] value for the given [key].
921          *
922          * @param key The key to set the [Float] value for.
923          * @param value The [Float] value to set.
924          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
925          *   match the metadata specification associated with the [key].
926          */
927         public fun setFloat(key: String, value: Float): Builder {
928             spec?.validateWriteRequest(key, Float::class.java, isCollection = false)
929             genericDocumentBuilder.setPropertyDouble(key, value.toDouble())
930             return this
931         }
932 
933         /**
934          * Sets a [Double] value for the given [key].
935          *
936          * @param key The key to set the [Double] value for.
937          * @param value The [Double] value to set.
938          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
939          *   match the metadata specification associated with the [key].
940          */
941         public fun setDouble(key: String, value: Double): Builder {
942             spec?.validateWriteRequest(key, Double::class.java, isCollection = false)
943             genericDocumentBuilder.setPropertyDouble(key, value)
944             return this
945         }
946 
947         /**
948          * Sets an [Int] value for the given [key].
949          *
950          * @param key The key to set the [Int] value for.
951          * @param value The [Int] value to set.
952          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
953          *   match the metadata specification associated with the [key].
954          */
955         public fun setInt(key: String, value: Int): Builder {
956             spec?.validateWriteRequest(key, Int::class.java, isCollection = false)
957             genericDocumentBuilder.setPropertyLong(key, value.toLong())
958             return this
959         }
960 
961         /**
962          * Sets a [Long] value for the given [key].
963          *
964          * @param key The key to set the [Long] value for.
965          * @param value The [Long] value to set.
966          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
967          *   match the metadata specification associated with the [key].
968          */
969         public fun setLong(key: String, value: Long): Builder {
970             spec?.validateWriteRequest(key, Long::class.java, isCollection = false)
971             genericDocumentBuilder.setPropertyLong(key, value)
972             return this
973         }
974 
975         /**
976          * Sets a [String] value for the given [key].
977          *
978          * @param key The key to set the [String] value for.
979          * @param value The [String] value to set.
980          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
981          *   match the metadata specification associated with the [key].
982          */
983         public fun setString(key: String, value: String): Builder {
984             spec?.validateWriteRequest(key, String::class.java, isCollection = false)
985             genericDocumentBuilder.setPropertyString(key, value)
986             return this
987         }
988 
989         /**
990          * Sets an [AppFunctionData] value for the given [key].
991          *
992          * @param key The key to set the [AppFunctionData] value for.
993          * @param value The [AppFunctionData] value to set.
994          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
995          *   match the metadata specification associated with the [key].
996          */
997         public fun setAppFunctionData(key: String, value: AppFunctionData): Builder {
998             spec?.validateWriteRequest(key, AppFunctionData::class.java, isCollection = false)
999             spec?.getPropertyObjectSpec(key)?.validateDataSpecMatches(value)
1000 
1001             genericDocumentBuilder.setPropertyDocument(key, value.genericDocument)
1002             if (!value.extras.isEmpty()) {
1003                 extrasBuilder.putBundle(extrasKey(key), value.extras)
1004             }
1005             return this
1006         }
1007 
1008         /**
1009          * Sets a [PendingIntent] value for the given [key].
1010          *
1011          * @param key The key to set the [AppFunctionData] value for.
1012          * @param value The [AppFunctionData] value to set.
1013          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
1014          *   match the metadata specification associated with the [key].
1015          */
1016         public fun setPendingIntent(key: String, value: PendingIntent): Builder {
1017             spec?.validateWriteRequest(key, PendingIntent::class.java, isCollection = false)
1018             extrasBuilder.putParcelable(key, value)
1019             return this
1020         }
1021 
1022         /**
1023          * Sets a [BooleanArray] value for the given [key].
1024          *
1025          * @param key The key to set the [BooleanArray] value for.
1026          * @param value The [BooleanArray] value to set.
1027          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
1028          *   match the metadata specification associated with the [key].
1029          */
1030         public fun setBooleanArray(key: String, value: BooleanArray): Builder {
1031             spec?.validateWriteRequest(key, Boolean::class.java, isCollection = true)
1032             genericDocumentBuilder.setPropertyBoolean(key, *value)
1033             return this
1034         }
1035 
1036         /**
1037          * Sets a [FloatArray] value for the given [key].
1038          *
1039          * @param key The key to set the [DoubleArray] value for.
1040          * @param value The [FloatArray] value to set.
1041          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
1042          *   match the metadata specification associated with the [key].
1043          */
1044         public fun setFloatArray(key: String, value: FloatArray): Builder {
1045             spec?.validateWriteRequest(key, Float::class.java, isCollection = true)
1046             genericDocumentBuilder.setPropertyDouble(
1047                 key,
1048                 *(value.asList().map { it.toDouble() }.toDoubleArray())
1049             )
1050             return this
1051         }
1052 
1053         /**
1054          * Sets a [DoubleArray] value for the given [key].
1055          *
1056          * @param key The key to set the [DoubleArray] value for.
1057          * @param value The [DoubleArray] value to set.
1058          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
1059          *   match the metadata specification associated with the [key].
1060          */
1061         public fun setDoubleArray(key: String, value: DoubleArray): Builder {
1062             spec?.validateWriteRequest(key, Double::class.java, isCollection = true)
1063             genericDocumentBuilder.setPropertyDouble(key, *value)
1064             return this
1065         }
1066 
1067         /**
1068          * Sets an [IntArray] value for the given [key].
1069          *
1070          * @param key The key to set the [IntArray] value for.
1071          * @param value The [IntArray] value to set.
1072          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
1073          *   match the metadata specification associated with the [key].
1074          */
1075         public fun setIntArray(key: String, value: IntArray): Builder {
1076             spec?.validateWriteRequest(key, Int::class.java, isCollection = true)
1077             genericDocumentBuilder.setPropertyLong(
1078                 key,
1079                 *(value.asList().map { it.toLong() }.toLongArray())
1080             )
1081             return this
1082         }
1083 
1084         /**
1085          * Sets a [LongArray] value for the given [key].
1086          *
1087          * @param key The key to set the [LongArray] value for.
1088          * @param value The [LongArray] value to set.
1089          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
1090          *   match the metadata specification associated with the [key].
1091          */
1092         public fun setLongArray(key: String, value: LongArray): Builder {
1093             spec?.validateWriteRequest(key, Long::class.java, isCollection = true)
1094             genericDocumentBuilder.setPropertyLong(key, *value)
1095             return this
1096         }
1097 
1098         /**
1099          * Sets a [ByteArray] value for the given [key].
1100          *
1101          * @param key The key to set the [ByteArray] value for.
1102          * @param value The [ByteArray] value to set.
1103          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
1104          *   match the metadata specification associated with the [key].
1105          */
1106         public fun setByteArray(key: String, value: ByteArray): Builder {
1107             spec?.validateWriteRequest(key, Byte::class.java, isCollection = true)
1108             genericDocumentBuilder.setPropertyBytes(key, value)
1109             return this
1110         }
1111 
1112         /**
1113          * Sets a [List] of [String] value for the given [key].
1114          *
1115          * @param key The key to set the [List] of [String] value for.
1116          * @param value The [List] of [String] value to set.
1117          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
1118          *   match the metadata specification associated with the [key].
1119          */
1120         public fun setStringList(key: String, value: List<String>): Builder {
1121             spec?.validateWriteRequest(key, String()::class.java, isCollection = true)
1122             genericDocumentBuilder.setPropertyString(key, *value.toTypedArray())
1123             return this
1124         }
1125 
1126         /**
1127          * Sets a [List] of [AppFunctionData] value for the given [key].
1128          *
1129          * @param key The key to set the [List] of [AppFunctionData] value for.
1130          * @param value The [List] of [AppFunctionData] value to set.
1131          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
1132          *   match the metadata specification associated with the [key].
1133          */
1134         public fun setAppFunctionDataList(key: String, value: List<AppFunctionData>): Builder {
1135             spec?.validateWriteRequest(key, AppFunctionData::class.java, isCollection = true)
1136             genericDocumentBuilder.setPropertyDocument(
1137                 key,
1138                 *value.map { it.genericDocument }.toTypedArray(),
1139             )
1140             value.forEachIndexed { index, element ->
1141                 spec?.getPropertyObjectSpec(key)?.validateDataSpecMatches(element)
1142                 if (!element.extras.isEmpty()) {
1143                     extrasBuilder.putBundle(extrasKey(key, index), element.extras)
1144                 }
1145             }
1146             return this
1147         }
1148 
1149         /**
1150          * Sets a [List] of [PendingIntent] value for the given [key].
1151          *
1152          * @param key The key to set the [List] of [AppFunctionData] value for.
1153          * @param value The [List] of [AppFunctionData] value to set.
1154          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
1155          *   match the metadata specification associated with the [key].
1156          */
1157         public fun setPendingIntentList(key: String, value: List<PendingIntent>): Builder {
1158             spec?.validateWriteRequest(key, PendingIntent::class.java, isCollection = true)
1159             extrasBuilder.putParcelableArrayList(key, ArrayList<PendingIntent>(value))
1160             return this
1161         }
1162 
1163         /**
1164          * Sets a generic type [value] of class [valueClass] for the given [key].
1165          *
1166          * @param T The type [value].
1167          * @param key The key to set the generic type [value] for.
1168          * @param value The generic type value to set.
1169          * @param valueClass The class of type [T].
1170          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
1171          *   match the metadata specification associated with the [key].
1172          */
1173         @RestrictTo(LIBRARY_GROUP)
1174         public fun <T : Any?> setGenericField(
1175             key: String,
1176             value: T,
1177             valueClass: Class<T>
1178         ): Builder {
1179             if (value == null) {
1180                 return this
1181             }
1182             @Suppress("UNCHECKED_CAST")
1183             return when (valueClass) {
1184                 Int::class.java -> setInt(key, value as Int)
1185                 Long::class.java -> setLong(key, value as Long)
1186                 Float::class.java -> setFloat(key, value as Float)
1187                 Double::class.java -> setDouble(key, value as Double)
1188                 Boolean::class.java -> setBoolean(key, value as Boolean)
1189                 String::class.java -> setString(key, value as String)
1190                 PendingIntent::class.java -> setPendingIntent(key, value as PendingIntent)
1191                 IntArray::class.java -> setIntArray(key, value as IntArray)
1192                 LongArray::class.java -> setLongArray(key, value as LongArray)
1193                 FloatArray::class.java -> setFloatArray(key, value as FloatArray)
1194                 DoubleArray::class.java -> setDoubleArray(key, value as DoubleArray)
1195                 BooleanArray::class.java -> setBooleanArray(key, value as BooleanArray)
1196                 ByteArray::class.java -> setByteArray(key, value as ByteArray)
1197                 // TODO(b/408385427): Handle serialization in generic factory
1198                 else -> setAppFunctionData(key, serialize(value, valueClass as Class<Any>))
1199             }
1200         }
1201 
1202         /**
1203          * Sets a list generic type [value] of class [itemValueClass] for the given [key].
1204          *
1205          * @param I The [value]'s item type.
1206          * @param T The type [value].
1207          * @param key The key to set the generic type [value] for.
1208          * @param value The generic type value to set.
1209          * @param itemValueClass The [value]'s item class.
1210          * @throws IllegalArgumentException if the [key] is not allowed or the [value] does not
1211          *   match the metadata specification associated with the [key].
1212          */
1213         @RestrictTo(LIBRARY_GROUP)
1214         public fun <I, T : List<*>?> setGenericListField(
1215             key: String,
1216             value: T,
1217             itemValueClass: Class<I>
1218         ): Builder {
1219             if (value == null) {
1220                 return this
1221             }
1222             @Suppress("UNCHECKED_CAST")
1223             return when (itemValueClass) {
1224                 String::class.java -> setStringList(key, value as List<String>)
1225                 PendingIntent::class.java -> setPendingIntentList(key, value as List<PendingIntent>)
1226                 // TODO(b/408385427): Handle serialization in generic factory
1227                 else ->
1228                     setAppFunctionDataList(
1229                         key,
1230                         value.map { serialize(it as Any, itemValueClass as Class<Any>) }
1231                     )
1232             }
1233         }
1234 
1235         /** Builds [AppFunctionData] */
1236         public fun build(): AppFunctionData {
1237             // TODO(b/399823985): validate required fields.
1238             return AppFunctionData(spec, genericDocumentBuilder.build(), extrasBuilder)
1239         }
1240     }
1241 
1242     public companion object {
1243         private const val DEFAULT_BOOLEAN: Boolean = false
1244         private const val DEFAULT_FLOAT: Float = 0F
1245         private const val DEFAULT_DOUBLE: Double = 0.0
1246         private const val DEFAULT_INT: Int = 0
1247         private const val DEFAULT_LONG: Long = 0L
1248 
1249         private fun extrasKey(key: String) = "property/$key"
1250 
1251         private fun extrasKey(key: String, index: Int) = "property/$key[$index]"
1252 
1253         // TODO(b/399823985): Codegen the mapping table to prevent using reflection
1254         private fun <T : Any> getSerializableClass(qualifiedName: String): Class<T> {
1255             return try {
1256                 @Suppress("UNCHECKED_CAST")
1257                 Class.forName(qualifiedName) as Class<T>
1258             } catch (e: Exception) {
1259                 Log.d(APP_FUNCTIONS_TAG, "Unable to find serializable class $qualifiedName", e)
1260                 throw IllegalArgumentException("Unable to find serializable class $qualifiedName")
1261             }
1262         }
1263 
1264         // TODO(b/399823985): Codegen the mapping table to prevent using reflection
1265         private fun <T : Any> getSerializableFactory(
1266             serializableClass: Class<T>
1267         ): AppFunctionSerializableFactory<T> {
1268             val packageName = getPackageName(serializableClass)
1269             val serializableSimpleName = serializableClass.simpleName
1270 
1271             val factorySimpleName = "${'$'}${serializableSimpleName}Factory"
1272             val factoryClassName = "${packageName}.${factorySimpleName}"
1273 
1274             return try {
1275                 val factoryClass = Class.forName(factoryClassName)
1276                 @Suppress("UNCHECKED_CAST")
1277                 factoryClass.getDeclaredConstructor().newInstance()
1278                     as AppFunctionSerializableFactory<T>
1279             } catch (e: Exception) {
1280                 Log.d(
1281                     APP_FUNCTIONS_TAG,
1282                     "Unable to create AppFunctionSerializableFactory for $serializableClass",
1283                     e
1284                 )
1285                 throw IllegalArgumentException(
1286                     "Unable to create AppFunctionSerializableFactory for $serializableClass"
1287                 )
1288             }
1289         }
1290 
1291         private fun getPackageName(serializableClass: Class<*>): String {
1292             val setOfProxyTypes = setOf(LocalDateTime::class.simpleName, Uri::class.simpleName)
1293             val serializableProxyPackageName = "androidx.appfunctions.internal.serializableproxies"
1294             if (setOfProxyTypes.contains(serializableClass.simpleName)) {
1295                 return serializableProxyPackageName
1296             }
1297 
1298             return serializableClass.packageName
1299         }
1300 
1301         /**
1302          * Serializes [serializable] to an [AppFunctionData].
1303          *
1304          * @param serializable The instance of [serializableClass].
1305          * @param serializableClass The class of [serializable].
1306          * @return [AppFunctionData] with properties from [serializable].
1307          * @throws IllegalArgumentException If unable to serialize [serializable] to an
1308          *   [AppFunctionData].
1309          * @see [AppFunctionSerializable]
1310          */
1311         @JvmStatic
1312         public fun <T : Any> serialize(
1313             serializable: T,
1314             serializableClass: Class<T>
1315         ): AppFunctionData {
1316             return try {
1317                 val factory = getSerializableFactory(serializableClass)
1318                 factory.toAppFunctionData(serializable)
1319             } catch (e: Exception) {
1320                 Log.d(
1321                     APP_FUNCTIONS_TAG,
1322                     "Something went wrong while serialize $serializable of class $serializableClass",
1323                     e
1324                 )
1325                 throw IllegalArgumentException(
1326                     "Unable to serialize $serializableClass. Is the class annotated with @AppFunctionSerializable?"
1327                 )
1328             }
1329         }
1330 
1331         /**
1332          * Serializes [serializable] to an [AppFunctionData].
1333          *
1334          * @param serializable The instance of [qualifiedName].
1335          * @param qualifiedName The qualified name of the class [serializable].
1336          * @return [AppFunctionData] with properties from [serializable].
1337          * @throws IllegalArgumentException If unable to serialize [serializable] to an
1338          *   [AppFunctionData].
1339          * @see [AppFunctionSerializable]
1340          */
1341         @RestrictTo(LIBRARY_GROUP)
1342         public fun <T : Any> serialize(serializable: T, qualifiedName: String): AppFunctionData {
1343             return serialize(serializable, getSerializableClass<T>(qualifiedName))
1344         }
1345 
1346         /** Represents an empty [AppFunctionData]. */
1347         @JvmField
1348         public val EMPTY: AppFunctionData =
1349             AppFunctionData(
1350                 GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "").build(),
1351                 Bundle.EMPTY,
1352             )
1353     }
1354 }
1355