Follow this systematic procedure to migrate Android applications that use the
AppFunctions API in version 1.0.0-alpha09 and lower to the compile-time
@AppFunctionServiceEntryPoint architecture introduced in version
1.0.0-alpha10. Then follow the Upgrade to 1.0.0-beta01 section. Apps that
already use @AppFunctionServiceEntryPoint only need that section.
Architectural shift overview
In lower versions of the AppFunctions API, for example version 1.0.0-alpha09:
- Applications require separate dependencies for core functionality,
specifically
androidx.appfunctions:appfunctions, and service components, specificallyandroidx.appfunctions:appfunctions-service. - The application implements
AppFunctionConfiguration.Provideron itsApplicationclass. - You manually register enclosing class instantiation using
AppFunctionConfiguration.Builder().addEnclosingClassFactory(...). - You place metadata property tags directly under
<application>.
In version 1.0.0-alpha10 featuring @AppFunctionServiceEntryPoint:
- The core and service dependencies are consolidated into a single runtime
artifact,
androidx.appfunctions:appfunctions, which eliminates the need for the standaloneappfunctions-servicelibrary. - A dedicated wrapper class extending
AppFunctionServiceis annotated with@AppFunctionServiceEntryPointand Hilt's@AndroidEntryPointor an alternative dependency injection framework. - The KSP compiler generates a concrete service subclass and an XML metadata
schema file in
assets/. - The OS discovers and routes executions using a consolidated
<service>andapp_metadatadeclaration inAndroidManifest.xml.
Required changes from 1.0.0-alpha09 to the entry point API
When focusing solely on the mandatory API changes required by the new
@AppFunctionServiceEntryPoint architecture, the migration consists of four
strict requirements that you must complete:
- Build dependency consolidation: Remove the merged
appfunctions-servicedependency while retaining coreappfunctionsand the KSP compiler. - Service wrapper creation: Replace the legacy
AppFunctionConfiguration.Provideron theApplicationclass with an abstract class extendingAppFunctionService, annotated with@AppFunctionServiceEntryPoint. - Annotation and context decoupling: Move AppFunction annotations to
the new wrapper methods as
@AppFunctionDeclaration. DropAppFunctionContextparameters, becauseAppFunctionServiceinherits directly fromContext. - Manifest registration: Register the KSP-generated concrete service in
AndroidManifest.xmlwithBIND_APP_FUNCTION_SERVICE, theAppFunctionServiceintent filter, and metadata property tags.
Systematic migration steps
Consolidate AppFunctions build dependencies
Remove the standalone appfunctions-service library from your module build
files like build.gradle.kts and version catalog like libs.versions.toml. In
version 1.0.0-alpha10, all core service capabilities are consolidated directly
within the main appfunctions artifact.
// build.gradle.kts
dependencies {
implementation(libs.androidx.appfunctions)
- implementation(libs.androidx.appfunctions.service)
ksp(libs.androidx.appfunctions.compiler)
}
# libs.versions.toml
[versions]
appfunctions = "1.0.0-beta01"
[libraries]
androidx-appfunctions = { module = "androidx.appfunctions:appfunctions", version.ref = "appfunctions" }
- androidx-appfunctions-service = { module = "androidx.appfunctions:appfunctions-service", version.ref = "appfunctions" }
androidx-appfunctions-compiler = { module = "androidx.appfunctions:appfunctions-compiler", version.ref = "appfunctions" }
Create a dedicated wrapper service extending AppFunctionService
Instead of annotating standalone business logic classes or implementing manual
configuration providers, create an abstract service wrapper across your project,
for example BaseAppFunctionService, extending AppFunctionService and
annotated with @AppFunctionServiceEntryPoint.
Recommended approach using Hilt
Annotate your service with @AndroidEntryPoint and inject your data
repositories or use cases using standard @Inject internal lateinit var:
@RequiresApi(36) @AndroidEntryPoint @AppFunctionServiceEntryPoint( serviceName = "MyAppFunctionService", appFunctionXmlFileName = "my_app_function_service", // Do NOT include .xml extension ) abstract class BaseAppFunctionService : AppFunctionService() { @Inject internal lateinit var messageRepository: MessageRepository @AppFunction(isDescribedByKDoc = true) internal suspend fun send( name: String, endpointValue: String, messageBody: String, ): MessageResult { return messageRepository.send(name, endpointValue, messageBody) } }
Framework-agnostic approach using alternative dependency injection or a service locator
While Hilt is recommended, many Android applications implement AppFunctions with
alternative dependency injection frameworks like Koin, Anvil, or manual Service
Locators. Because AppFunctionService inherits from Android
android.app.Service and therefore Context, you are able access your
application's DI container directly through applicationContext in property
getters or during service lifecycle execution:
@RequiresApi(36) @AppFunctionServiceEntryPoint( serviceName = "ServiceLocatorMyAppFunctionService", appFunctionXmlFileName = "service_locator_my_app_function_service", ) abstract class ServiceLocatorBaseAppFunctionService : AppFunctionService() { // Example using a manual Service Locator or Application container private val messageRepository by lazy { (applicationContext as AppFunctionApplication).appContainer.messageRepository } // Or with Koin / alternative DI locators: // private val messageRepository: MessageRepository by inject() @AppFunction(isDescribedByKDoc = true) internal suspend fun send( name: String, endpointValue: String, messageBody: String, ): MessageResult { return messageRepository.send(name, endpointValue, messageBody) } }
Simplify method signatures and decouple context
Remove legacy AppFunctionContext parameters from your core methods. When a
method requires an Android Context, for example when constructing a
PendingIntent, access this directly from your AppFunctionService wrapper
because the wrapper inherently extends android.content.Context.
- suspend fun makeCall(appFunctionContext: AppFunctionContext, contactName: String?): PendingIntent
+ suspend fun makeCall(contactName: String?): PendingIntent
Remove legacy configuration provider
Update your Application class by removing
AppFunctionConfiguration.Provider and its associated builder entry points:
- abstract class BaseChatApplication : Application(), AppFunctionConfiguration.Provider { ... }
+ abstract class BaseChatApplication : Application()
Avoid redundant abstraction layers
Don't attempt to make an AppFunction class or method OS-agnostic—AppFunctions
are inherently part of the Android platform integration through the
androidx.appfunctions package. For architectural cleanliness, use existing
application functionality, such as existing repositories, use cases, or domain
orchestrators, to execute the behavior within the @AppFunctionDeclaration
methods rather than creating redundant abstraction layers around the OS service.
Consolidate service and metadata manifest declarations
Register the KSP-generated service declaration and app_metadata property
inside your module manifest, for example in src/main/AndroidManifest.xml
within the <application> tag:
<service android:name="com.example.snippets.ai.MyAppFunctionService" android:permission="android.permission.BIND_APP_FUNCTION_SERVICE" android:exported="true" tools:targetApi="36"> <property android:name="android.app.appfunctions.schema" android:value="app_functions_schema.xsd" /> <property android:name="android.app.appfunctions.v2" android:value="my_app_function_service.xml" /> <intent-filter> <action android:name="android.app.appfunctions.AppFunctionService" /> </intent-filter> </service> <property android:name="android.app.appfunctions.app_metadata" android:resource="@xml/app_metadata" />
Set android:name to the class that KSP generates from serviceName, and
the android.app.appfunctions.v2 value to appFunctionXmlFileName plus
.xml.
Upgrade to 1.0.0-beta01
Apply these changes to every app that uses @AppFunctionServiceEntryPoint,
including apps migrated with the preceding steps:
- Annotation rename: Replace each
@AppFunctionannotation with@AppFunctionDeclaration, and update its import. Match the whole name, so annotations such as@AppFunctionSerializablestay unchanged. Version 1.0.0-alpha13 renamed this annotation. - Explicit KDoc flag: Set
isDescribedByKDoc = trueon every@AppFunctionDeclaration,@AppFunctionSerializable, and@AppFunctionSignature. Since 1.0.0-beta01, it has no default value. - App description attribute: In
app_metadata.xml, renameappfn:descriptiontoappfn:instructions. - Imports: Import
AppFunctionNamefromandroidx.appfunctions, not fromandroidx.appfunctions.metadata. - Removed APIs: Remove remaining uses of
AppFunctionConfigurationandAppFunctionContext. Version 1.0.0-alpha12 removed them. - Other API changes: If the app uses these APIs, update them:
@AppFunctionStringValueConstraint: Replace regular expression patterns withpatternMatchers(1.0.0-beta01).AppFunctionResourceContainerandAppFunctionTextResource: Opt in with@OptIn(ExperimentalAppFunctionsApi::class)(1.0.0-beta01).AppFunctionAppMetadata.description: Readinstructionsinstead (1.0.0-beta01).AppFunctionMetadata.isEnabled: CallAppFunctionManager.getAppFunctionStatesinstead (1.0.0-alpha11).ObserveAppFunctionsEvent: UseAppFunctionsChangeEventinstead (1.0.0-alpha13).
Verification and troubleshooting
- Clean rebuild and deploy:
bash ./gradlew clean installDebug Verify AppSearch discovery / indexing: Run the following ADB command to confirm the OS successfully discovered and indexed your functions:
bash adb shell cmd app_function list-app-functions --package com.example.chatappIf the output is empty, confirm thatandroid.app.appfunctions.v2matches the exact asset name generated inassets/.Verify execution using ADB:
bash adb shell "cmd app_function execute-app-function \ --package com.example.chatapp \ --function 'com.example.chatapp.appfunctions.BaseAppFunctionService#send' \ --parameters '{\"name\": \"Alice\", \"endpointValue\": \"1\", \"messageBody\": \"Hello Alice!\"}'"