Third-party Integration with Android Service v1.x¶
Note
For API v1.0 and API v1.1 users, support will be removed in future releases of the Android Service. Please consider switching to API v1.2.1 as soon as possible. See migration strategies for recommended ways to stay up-to-date.
UF Android Service has a set of APIs that allow a third-party application to interact with it.
To integrate with UF Android Service, your application must:
- Import the
uf-service-apilibrary (see the uf-service-api section). - Bind to the
uf-android-serviceservice (see the Bind to uf-android-service section).
The UF Service API Reference Implementation is provided as an example of a third-party application.
The uf-service-api-reference-implementation is able to:
- Show the current service status.
- Open the preferences page of
uf-android-service(Settings page). - Grant authorization to execute an action during a soft update.
API Compatibility¶
Newer APIs are only supported by recent versions of UF Android Service. The compatibility between the APIs and the service versions is specified in the following table:
| API | UF Android Service |
|---|---|
| v0.1 | any |
| v1.0 | ≥ v1.0.0 (version code 23) |
| v1.1 | ≥ v1.3.0 (version code 31) |
| v1.2 | ≥ v1.4.0 (version code 33) |
| v1.2.1 | ≥ v1.4.0 (version code 33) |
See the packages page for the recommended strategies to adopt the latest API in your application and concurrently update UF Android Service.
Note
API v1.2.1 is backward compatible with UF Android Service down to v1.4.0, but the API addition is only useful when UF Android Service ≥ v1.6.0 is running on the device, as specified in the API v1.2.1 migration notes.
Migration from API v1.2 to v1.2.1¶
To migrate your application from API v1.2 to v1.2.1:
- Upgrade the uf-service-api dependency to v1.6.1.
- If you're checking the availability of updates using Force Ping, check for either Idle (sent by UF Android Service < v1.6.0) or NoNewState events (sent by UF Android Service ≥ v1.6.0).
- (Recommended) Avoid sending AuthorizationResponse messages with
granted = false.
Migration from API v1.1 to v1.2¶
To migrate your application from API v1.1 to v1.2:
- Upgrade the uf-service-api dependency to 1.5.1.
- Handle the new service events (Started, Stopped, CantBeStopped, ConfigurationUpdated, NewTargetTokenReceived) when your app receives the ServiceNotification.
Migration from API v1.0 to v1.1¶
To migrate your application from API v1.0 to v1.1:
- Upgrade the uf-service-api dependency to v1.3.0.
- Use the ConfigureServiceV2 message to configure the client instead of the ConfigureService message (still supported but deprecated).
- Handle the CurrentServiceConfigurationV2 message in your handler.
The service sends the
CurrentServiceConfigurationV2message in response to a sync request instead of the CurrentServiceConfiguration message. - Handle the new WaitingUpdateWindow service state when your app receives the ServiceNotification.
Bind to uf-android-service¶
uf-android-service is a bound service;
it allows components (such as activities) to bind to the service, send requests,
receive responses, and perform inter-process communication (IPC).
To bind with the service, create an intent with:
action = UFServiceInfo.SERVICE_ACTIONpackage = UFServiceInfo.SERVICE_PACKAGE_NAME
This is an example of how to bind an application with the service:
private val mService: Messenger
private val mConnection = object : ServiceConnection {
override fun onServiceConnected(
className: ComponentName,
service: IBinder
) {
mService = Messenger(service)
Toast.makeText(this@MainActivity, R.string.ui_connected,
Toast.LENGTH_SHORT).show()
handleRemoteException {
mService!!.send(Communication.V1.In.RegisterClient(mMessenger).toMessage())
mService!!.send(Communication.V1.In.Sync(mMessenger).toMessage())
}
mIsBound = true
}
override fun onServiceDisconnected(className: ComponentName) {
Log.i(TAG, "Service is disconnected")
doUnbindService()
}
override fun onBindingDied(name: ComponentName?) {
Log.i(TAG, "Service binding has died")
mIsBound = false
}
}
private fun doBindService() {
val intent = Intent(UFServiceInfo.SERVICE_ACTION)
intent.setPackage(UFServiceInfo.SERVICE_PACKAGE_NAME)
intent.flags = FLAG_INCLUDE_STOPPED_PACKAGES
val serviceExists = bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
if (!serviceExists && !mServiceExist) {
Toast.makeText(applicationContext, "UpdateFactoryService not found", Toast.LENGTH_LONG).show()
unbindService(mConnection)
this.finish()
} else {
mServiceExist = true
}
}
private fun doUnbindService() {
if (mIsBound) {
if (mService != null) {
try {
mService!!.send(Communication.V1.In.UnregisterClient(mMessenger).toMessage())
} catch (e: RemoteException) {
}
}
unbindService(mConnection)
mIsBound = false
}
}
Messages¶
UF Android Service uses Android Messenger to receive user inputs and to notify its status.
The uf-service-api provides a set of helper classes that must be used to communicate with the service
(see Communication).
For more details, see the official documentation.
uf-service-api¶
The uf-service-api contains all the classes that a third-party application must include to handle communication with uf-android-service.
uf-service-api contains:
- UFServiceInfo: An object class with the main information about the service.
- Communication: A set of classes to build (parse) messages to send to (or receive from) the service.
- UFServiceConfiguration: A data class that contains the current configuration of the service.
- UFServiceMessageV1: A set of classes that represents the state of the service.
To import this module:
- Contact us to receive the latest AAR file.
- Put the AAR file inside the
<project_root>/app/libsdirectory. - Add the following line to the
<project_root>/app/build.gradlefile:implementation(fileTree(include: ['*.aar'], dir: 'libs')) - Sync Gradle.
The full API reference documentation will be provided along with the AAR file.
UFServiceInfo¶
UFServiceInfo is an object class with the main information about the service.
object UFServiceInfo {
/**
* Package name of the Update Factory Service.
*/
const val SERVICE_PACKAGE_NAME = "com.kynetics.uf.service"
/**
* Action to bind with the Update Factory Service.
*/
const val SERVICE_ACTION = "com.kynetics.action.BIND_UF_SERVICE"
/**
* Action to open the Update Factory Settings.
*/
const val ACTION_SETTINGS = "com.kynetics.action.SETTINGS"
}
UFServiceConfigurationV2¶
UFServiceConfigurationV2 contains:
tenant: The name of the tenant.controllerId: The device's unique identifier.url: The URL of the Update Factory server.targetToken: The device token to communicate with the UF server.gatewayToken: A token shared between all targets of the tenant to communicate with the UF server.isApiMode: Iffalse, the UF Android Service shows a pop-up window when user authorization is needed. Iftrue, the UF Android Service sends an AuthorizationRequest message via the API.isEnable:trueto enable the UF Android background service,falseto disable it.isUpdateFactoryServe: Iftrue, the service is optimized for Update Factory; iffalse, the service is optimized for hawkBit.targetAttributes: A map of string (key) to string (value) device attributes that the Android Service sends to the UF server. AddTargetAttributes is the recommended way to provide attributes when using API ≥ 1.2.timeWindows: Configuration of the time slots during which a forced update can be applied on the device. The time slots are defined using a cron expression and a window size duration:cronExpression: A cron expression (QUARTZ format) that defines when the time window begins (in the device's time zone).duration: The window size in seconds.
The targetAttributes and timeWindows properties can be omitted when building a UFServiceConfigurationV2, as they have a default value.
UFServiceConfigurationV2(
tenant = <tenant_name>,
controllerId = <controller_id>,
url = <uf_server_url>,
targetToken = <target_token>,
gatewayToken = <gateway_token>,
isApiMode = true,
isEnable = true,
isUpdateFactoryServe = true,
targetAttributes = mutableMapOf("DeviceOS" to "Android"),
timeWindows = TimeWindows(
cronExpression = "* * * ? * *",
duration = 3600
)
)
Communication¶
Communication classes are organized hierarchically:
Communication.V1.[<In> | <Out>].<message_type>
In classes represent all messages that can be sent to the service, while Out classes represent all messages that
can be received from the service.
In¶
To send a message to the UF service, you must create an instance of an In class and then invoke the toMessage method.
//mService:Messenger;
mService!!.send(Communication.V1.In.RegisterClient(mMessenger).toMessage())
ConfigureServiceV2¶
A class to build a message to configure the service.
Arguments:
conf: The new service configuration (an instance ofUFServiceConfigurationV2).
RegisterClient¶
A class to build a message to register the client (the service will notify the client of any updates).
Constructor Arguments:
replyTo: The Messenger to reply to.
UnregisterClient¶
A class to build a message to unregister the client from the service.
Constructor Arguments:
replyTo: The Messenger to reply to.
AuthorizationResponse¶
A class to build a message to grant authorization to download or apply an update.
Constructor Arguments:
granted:trueto immediately grant authorization. Providingfalseis discouraged and has different behaviors depending on the UF Android Service version (see Note).
Note
Regardless of the service version, the recommended implementation is to send granted = true when ready to provide authorization to proceed.
Since the service is blocked while waiting for authorization, there is no need to provide an explicit denial.
The handling of the granted = false message in UF Android Service has changed since v1.6.0.
In UF Android Service v1.6.0 or later, any AuthorizationResponse message with granted = false will be ignored.
The service keeps waiting for authorization until an AuthorizationResponse message with granted = true is sent.
In UF Android Service < v1.6.0, an AuthorizationResponse message with granted = false will cause subsequent AuthorizationResponse messages with granted = true to be ignored until a force ping is called or the device is rebooted.
Sync¶
A class to build a message that requires a sync with the service. The service will respond with its configuration and state (see the Out section).
Constructor Arguments:
replyTo: The Messenger to reply to.
ForcePing¶
A class to build a message that forces a poll to the Update Factory server.
Note
In UF Android Service version ≥ 1.6.0, the ForcePing behavior has been updated:
if the state on the server is unchanged, the service will broadcast NoNewState;
otherwise, it will broadcast the new state as before. NoNewState is only sent in case of a ForcePing.
When using the following combination:
* version < 1.6.0 (API v1.2 or earlier) of the `uf-service-api` library
* version ≥ 1.6.0 (including prereleases) of UF Android Service
`NoNewState` cannot be decoded and causes a harmless warning message.
To correctly handle `NoNewState`, [migrate your application to use API v1.2.1](#migration-from-api-v12-to-v121).
AddTargetAttributes¶
A class to build a message that adds target attributes.
Constructor Arguments:
targetAttributesWithPolicy: A map of target attributes with anAggregationPolicy.
Target attributes are defined by three sources:
- The built-in attributes.
- The UF Android Service configuration.
- The
targetAttributesadded with this API.
Conflicts on target attributes are handled as follows:
- Built-in target attributes always win.
- Target attributes defined by
AddTargetAttributeswin over target attributes provided during UF Android Service configuration.
Out¶
When you receive a message from the service, you must use the toOutV1Message() (see Kotlin extension function)
function, which returns an instance of the Out class.
override fun handleMessage(msg: Message) {
runCatching{
val v1Msg = msg.toOutV1Message()
when (v1Msg) {
is Communication.V1.Out.CurrentServiceConfigurationV2 -> handleServiceConfigurationMsg(v1Msg)
is Communication.V1.Out.AuthorizationRequest -> handleAuthorizationRequestMsg(v1Msg)
is Communication.V1.Out.ServiceNotification -> handleServiceNotificationMsg(v1Msg)
else -> { Log.i(TAG, "Unexpected message received") }
}
}.onFailure{ error ->
Log.w(TAG, "Unexpected message received", error)
}
}
ServiceNotification¶
ServiceNotification contains the current status of the service. It has a field called content of type UFServiceMessageV1.
AuthorizationRequest¶
AuthorizationRequest has a field called authName that contains the type of authorization (typically either "download" or "update").
CurrentServiceConfigurationV2¶
CurrentServiceConfigurationV2 has a field called conf that contains the current service configuration (an instance of UFServiceConfigurationV2).
UFServiceMessageV1¶
Classes that map the states and events of UF Android Service.
States:¶
Downloading¶
The client is downloading artifacts from the server.
Fields:
artifacts: A list of artifacts to download.
Artifact¶
Fields:
name: The artifact name.size: The artifact size.md5: The artifact's MD5 checksum.
Updating¶
The update process has started. Any request to cancel an update will be rejected.
CancellingUpdate¶
The last update request is being canceled.
WaitingDownloadAuthorization¶
Waiting for authorization to start the download.
WaitingUpdateAuthorization¶
Waiting for authorization to start the update.
WaitingUpdateWindow¶
Waiting for a valid time slot to apply the forced update.
Idle¶
The client is waiting for new requests from the server.
NoNewState¶
The state on the server is unchanged. NoNewState is only sent in case of a Force Ping.
ConfigurationError¶
Bad service configuration.
Fields:
details: Additional information.
Events:¶
Polling¶
The client is contacting the server to retrieve a new action to execute.
StartDownloadFile¶
A file download has started.
Fields:
fileName: The name of the file to download.
FileDownloaded¶
A file has been downloaded.
Fields:
fileDownloaded: The file that was downloaded.
DownloadProgress¶
The progress of the current file download process.
Progress is sent every minute if at least an additional 10% has been downloaded with respect to the previous progress message. Progress is sent for artifacts of any update type.
Fields:
fileName: The file being downloaded.percentage: The percentage downloaded (from 0 to 1).
AllFilesDownloaded¶
All needed files have been downloaded.
UpdateFinished¶
The update is finished.
Fields:
successApply:trueif the update was successfully applied,falseotherwise.details: Additional details.
Error¶
An error has occurred.
Fields:
details: Additional details.
UpdateProgress¶
The progress of the current update phase.
Progress is sent every minute if at least an additional 10% has been installed with respect to the previous progress message. Progress is only sent for double-copy system updates.
Fields:
phaseName: The name of the update phase.phaseDescription: The description of the update phase.percentage: The progress percentage of the update phase.
UpdateAvailable¶
An update is available on the cloud.
Fields:
id: The update ID.
Started¶
The service has started.
Fields:
configuration: The service configuration.
Stopped¶
The service has stopped.
Fields:
configuration: The service configuration.
CantBeStopped¶
The service can't be stopped. This typically happens when an update is currently being installed.
Fields:
configuration: The service configuration.retryIn: The number of milliseconds until the next service self-stop attempt.
ConfigurationUpdated¶
The configuration used by the service has been updated. This event is notified when the configuration change doesn't require a restart of the service.
Fields:
configuration: The new service configuration.
NewTargetTokenReceived¶
The client has received a new target token from the Update Factory server.
References¶
Kynetics - uf-service-api-reference-implementation repository