{"id":19079302,"url":"https://github.com/covesa/covesa-aosp-sdk","last_synced_at":"2025-10-12T03:47:43.799Z","repository":{"id":242539275,"uuid":"805404686","full_name":"COVESA/covesa-aosp-sdk","owner":"COVESA","description":null,"archived":false,"fork":false,"pushed_at":"2024-06-21T07:54:33.000Z","size":123,"stargazers_count":5,"open_issues_count":1,"forks_count":2,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-11-09T02:14:05.997Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/COVESA.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-24T13:56:16.000Z","updated_at":"2024-11-04T00:49:50.000Z","dependencies_parsed_at":"2024-06-03T17:24:09.689Z","dependency_job_id":null,"html_url":"https://github.com/COVESA/covesa-aosp-sdk","commit_stats":null,"previous_names":["covesa/covesa-aosp-sdk"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/COVESA%2Fcovesa-aosp-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/COVESA%2Fcovesa-aosp-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/COVESA%2Fcovesa-aosp-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/COVESA%2Fcovesa-aosp-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/COVESA","download_url":"https://codeload.github.com/COVESA/covesa-aosp-sdk/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":232242733,"owners_count":18493786,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T02:14:12.889Z","updated_at":"2025-10-12T03:47:38.762Z","avatar_url":"https://github.com/COVESA.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Status - Incubating](https://img.shields.io/static/v1?label=Status\u0026message=Incubating\u0026color=FEFF3A\u0026style=for-the-badge)\n\n# COVESA AOSP SDK\n\n## Objective of this SDK\nThe main goal of this SDK is to provide a set of libraries which allow third-party applications to access some automotive features in a way that is agnostic of manufacturer and model.\n\nExamples of such features include:\n1. Car features such as control over interior ambient lights or car data such as mileage which are not exposed by OEMs via [CarPropertyManager](https://developer.android.com/reference/android/car/hardware/property/CarPropertyManager)\n2. Common APIs for functions which are not supported by AOSP, such as the push notification API.\n\n## Standardization\nStandardization of different car APIs is an ongoing effort: https://wiki.covesa.global/display/WIK4/Automotive+AOSP+App+Framework+Standardization+Expert+Group\n\nPush notifications is the first standardized API within this group.\n\nThis SDK also provides an example of an AIDL-based communication between an OEM-implementable service and a third-party application. The example implements an ambient light API.\n\n## Versioning\nThe versioning strategy and stability promises are yet to be established.\n\n## SDK components\nThe SDK puts into communication 3 components:\n* **Client app**: the third party-app that wants to interact with manufacturers cars.\n* **Server app**: the service that each manufacturer needs to implement on their cars.\n* **AIDL**: it’s the interface used for remote communication between the client and server apps.\n\nThe AIDL definition for the SDK is contained in the [api/aidl](./api/aidl/) gradle module.\nThe official Google documentation about the AIDL format can be found [here](https://developer.android.com/develop/background-work/services/aidl).\n\nWhile client apps developers can directly use the AIDL to establish a connection with the SDK service, an utility layer is defined in the [api/client](./api/client/) gradle module. This layer hides most of the complexity of the AIDL connection by exposing a modern, more developer-friendly interface.\n\nThe same layer is used to expose APIs which do not depend on AIDL, such as push notifications.\n\n## Push notifications\nThird-party apps should link to the same single library `implementation(project(\":api:client\"))`. See [samples/client](./samples/client) for details. \n\nThere is no AIDL layer for push notifications. Instead of an AIDL-based system service, OEMs implement their own [distributor](https://unifiedpush.org/users/distributors/) which is reacting to a specific broadcast.\n\nIn order to send/receive push notifications a distributor is needed. In this case, we are using Sunup in order to be able to test. Sunup is already doing some work for us, since Mozilla push services are set by default.\n\nPush notifications are sent by the application server. Therefore the mobile application registers the push endpoint, and the encryption keys to this server.\n\nAdd the following dependency to your `build.gradle`:\n```\n(TBD)\n```\n\nCreate a class that implements `PushService` from our SDK:\n```kotlin\nclass PushServiceImpl: PushService() {\n    class NewRegistrationState(val registered: Boolean)\n\n    /**\n     * A new endpoint is to be used for sending push messages. The new endpoint\n     * should be send to the application server, and the app should sync for\n     * missing notifications.\n     */\n    override suspend fun onNewEndpoint(endpoint: PushEndpoint, instance: String) {\n        // TODO: Send the new endpoint to your web push server\n        updateRegistrationState(true)\n    }\n\n   /**\n     * A new message is received. The message contains the decrypted content of the push message\n     * for the instance\n     */\n    override suspend fun onMessage(message: PushMessage, instance: String) {\n        // TODO: Create and show a notification with the message received\n        updateRegistrationState(true)\n    }\n\n    /**\n     * The registration is not possible, eg. no network, depending on the reason,\n     * you can try to register again directly.\n     */\n    override suspend fun onRegistrationFailed(reason: FailedReason, instance: String) {\n      // TODO: Inform the user that the registration failed and therefore notifications\n      // are not available\n    }\n\n    /**\n     * This application is unregistered by the distributor from receiving push messages\n     */\n    override suspend fun onUnregistered(instance: String) {\n        // TODO: Send an unregister action to your web push server, removing the endpoint\n        updateRegistrationState(false)\n    }\n\n    /**\n     * Update the UI\n     */\n    private suspend fun updateRegistrationState(registered: Boolean) {\n        _events.emit(NewRegistrationState(registered))\n    }\n\n    companion object {\n        private const val TAG = \"PushServiceImpl\"\n        private val _events = MutableSharedFlow\u003cNewRegistrationState\u003e()\n        val events = _events.asSharedFlow()\n    }\n}\n```\n\nAdd your `PushService` implementation to your manifest:\n```xml\n\u003cservice android:name=\"global.covesa.sdk.client.push.PushServiceImpl\"\n    android:exported=\"false\"\u003e\n    \u003cintent-filter\u003e\n        \u003caction android:name=\"global.covesa.sdk.PUSH_EVENT\"/\u003e\n    \u003c/intent-filter\u003e\n\u003c/service\u003e\n```\n\n### Working with PushManager\nThe PushManager, as the name says, manages your app interactions with the push distributor. If you skip this step, your PushServiceImpl will never work.\n\n#### Register push service\nIn order to register you need to get the distributor first, so your app knows who distributes the push you receive/send.\nTo do that user the `tryUseCurrentOrDefaultDistributor` method from the `PushManager`:\n```kotlin\nPushManager.tryUseCurrentOrDefaultDistributor(activityContext) { success -\u003e\n    // TODO: Next step\n}\n```\nWith this method the SDK tries to use the saved distributor else, use the default distributor opening the deeplink \"unifiedpush://link\"\nIt can be used on application startup to register to the distributor.\nIf you had already registered to a distributor, this ensure the connection is working.\nIf the previous distributor has been uninstalled, it will fallback to the user's default.\nIf you register for the first time, it will use the user's default Distributor or the OS will ask what it should use.\nWhen a distributor is picked, you can then register:\n\n```kotlin\nPushManager.tryUseCurrentOrDefaultDistributor(activityContext) { success -\u003e\n  if (success) {\n    val vapidPubKey = \u003cYour vapid pub key\u003e\n    try {\n        PushManager.register(\n            activity,\n            vapid = vapidPubKey\n        )\n        Log.w(TAG, \"UnifiedPush registered with vapid $vapidPubKey\")\n    } catch (e: PushManager.VapidNotValidException) {\n        Log.w(TAG, \"UnifiedPush failed to register with vapid $vapidPubKey. With exception $e\")\n    }\n  }\n}\n```\n\n### Working with non-default distributor\nYou will probably want to allow the users to use another distributor but their default one.\n\nFor this, you can get the list of available distributors with `getDistributors`.\n\nOnce the user has chosen the distributor, you have to save it with `saveDistributor`. This function must be called before `register`.\n\nWhen the distributor is saved, you can call `register` to request a new registration. It has optional parameters, the following example uses `messageForDistributor` and `vapid`. You can use instance to bring multiple-registration support to your application.\n\n```kotlin\n// Get a list of distributors that are available\nval distributors = PushManager.getDistributors(context)\n// select one or ask the user which distributor to use, eg. with a dialog\nval userDistrib = yourFunc(distributors)\n// save the distributor\nPushManager.saveDistributor(context, userDistrib)\n// register your app to the distributor\nPushManager.register(context, messageForDistributor, vapid)\n// unregister to unsubscribe from the distributor\nPushManager.unregister(context)\n```\n\n#### Get systems available distributors\n```kotlin\nPushManager.getDistributors(context: Context) : List\u003cString\u003e\n```\n\n#### Save a new distributor\n```kotlin\nPushManager.saveDistributor(context: Context, distributor: String)\n```\n\n#### Get registered distributor\nGet the distributor registered by the user, but the distributor may not have respond yet to our requests. Most of the time getAckDistributor is preferred.\n```kotlin\nPushManager.getSavedDistributor(context: Context): String?\n```\n\nThe preferred way:\n```kotlin\nPushManager.getAckDistributor(context: Context): String?\n```\n\n#### Remove distributor\nUnregister all instances and remove the distributor.\n```kotlin\nPushManager.removeDistributor(context: Context)\n```\n\n#### Unsubscribe from a distributor\nTo unsubscribe, simply call `unregister`. Set the instance you want to unsubscribed to if you used one during registration.\nIt removes the distributor if this is the last instance to unregister.\n```kotlin\nPushManager.unregister(context: Context)\n```\n\n\n## API Artifacts\nBoth API libraries artifacts can be generated by using the following commands:\n\n```\n./gradlew :api:aidl:assemble\n./gradlew :api:client:assemble\n```\n\nThe generated `aar` library files can then be found in the `build/output/aar` folders for each module.\n\n## Integration with third-party apps\nThe generated `aar` libraries can be added as dependency for a third-party app by copying them in a folder in the app project tree and adding the following in the app module `build.gradle.kts`:\n\n```\ndependencies {\n    implementation(files(\"../libs/aidl-debug.aar\"))\n    implementation(files(\"../libs/client-debug.aar\"))\n    ...\n}\n```\n\n## Samples\nExample implementations for both the client and server libraries integrations can be found in the [samples](./samples) modules of this repo.\nEach sample contains its own README.md with more detailed information.\n\n## Client testing\nThird party app developers can test the library correct integration by installing the [server sample](./samples/server) in any Android emulator (even a non-automotive one) running a minimum Android SDK version as defined in the [library configuration](./gradle-plugins/src/main/kotlin/global/covesa/gradle/AndroidPlugin.kt#L11).\n\nThe server sample acts both as a reference for car manufacturers but also it logs every request received by client apps and responds updating each registered listener in a realistic way. That can be used to test third party apps behavior.\n\n### Maintainers\n* José Freitas - Appning\n* Nuno Palma - Appning\n* Rodrigo Fernandes - Appning\n* Viktor Mukha - Paradox Cat\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcovesa%2Fcovesa-aosp-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcovesa%2Fcovesa-aosp-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcovesa%2Fcovesa-aosp-sdk/lists"}