Third-party Integration
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 the UF Android Service your application you must:
- Import the module
uf-client-service-api
library (see Module uf-client-service-api section) - Bind to the uf-android-service service (see Bind to uf-android-service 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 supported only by recent versions of the 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 the UF Android Service.
Note
API v1.2.1 is backwards compatible with UF Android Service down to v1.4.0, but the API addition is useful only 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-client-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-client-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-client-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 CurrentServiceConfigurationV2 message as response of 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.
To bind with the service create an intent with:
- action =
UFServiceInfo.SERVICE_ACTION
- package =
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 is 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 serviceExist = bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
if (!serviceExist && !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¶
The Android client Service uses Android Messenger to receive user inputs and to notify its status. The uf-client-service-api provides a set of help classes that must be used to communicate with the service (see communication).
For more details see the official documentation.
Module uf-client-service-api¶
The module uf-client-service-api
contains all of the classes that a third party application must include to handle the communication with the uf-android-service
.
uf-client-service-api
contains:
- UFServiceInfo: object class with the main information about the service
- Communication: a set of classes to build (parse) messages to send (receive) to 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:
- configure your project to use jitpack (see jitpack documentation).
- add
implementation 'com.github.kynetics.uf-android-client:uf-client-service-api:v1.y.z'
to the project dependencies, for example:implementation 'com.github.kynetics.uf-android-client:uf-client-service-api:v1.6.1'
See API reference documentation for additional info.
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 tenantcontrollerId
= device unique identifierurl
= URL of Update Factory ServertargetToken
= device token to communicate with the UF servergatewayToken
= a token shared between all targets of the tenant to communicate with the UF ServerisApiMode
= 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 APIisEnable
=true
to enable the UF Android background Service,false
to disable itisUpdateFactoryServe
= iftrue
the service is optimized for Update Factory, iffalse
the service is optimized for hawkBittargetAttributes
= 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.2timeWindows
= configuration of the time slots during which a force update can be applied on the device, the timeSlots are defined using a cron expression and a windows size duration:cronExpression
= a cron expression (QUARTZ format) that defines when the time windows begins (in the device time zone)duration
= windows 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 hierarchy organized:
Communication.V1.[<In>
| <Out>
].<message_type>
In
classes represent all messages that can be sent to the service, instead, 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 method toMessage
//mService:Messenger;
mService!!.send(Communication.V1.In.RegisterClient(mMessenger).toMessage())
ConfigureServiceV2¶
Class to build a message to configure the service.
Arguments:
conf
: new service configuration (instance of UFServiceConfigurationV2)
RegisterClient¶
Class to build a message to register the client (the service will notify any update to the client)
Constructor Arguments:
replyTo
: Messenger where reply
UnregisterClient¶
Class to build a message to unregister the client to the service
Constructor Arguments:
replyTo
: Messenger where reply
AuthorizationResponse¶
Class to build a message to grant the authorization to download or apply an update.
Constructor Arguments:
granted
:true
to immediately grant authorization. Providingfalse
is 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 the authorization to proceed.
Since the Service is blocked waiting for the authorization there is no need to provide 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¶
Class to build a message which requires a sync with the service. The service will respond with its configuration and its state (see Out section).
Constructor Arguments:
replyTo
: Messenger where reply
ForcePing¶
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 in the server is unchanged, the service will broadcast the NoNewState
;
otherwise it will broadcast the new state as before. NoNewState
is sent only in case of Force Ping.
When using the following combination:
- version < 1.6.0 (API v1.2 or earlier) of the
uf-client-service-api
library - version ≥ 1.6.0 (including prereleases) of the UF Android Service
the NoNewState
cannot be decoded and causes a harmless warning message.
To correctly handle NoNewState
migrate your application to use API v1.2.1.
AddTargetAttributes¶
Class to build a message that adds target attributes.
Constructor Arguments:
targetAttributesWithPolicy
: a Map of target attributes with an AggregationPolicy
Target attributes are defined by three sources:
- the built-in attributes
- the UF Android Service configuration
- the targetAttributes added with this API
Conflicts on target attributes are handled as follows:
- built-in target attributes always win
- target attributes defined by
AddTargetAttributes
win over target attributes provided during UF Android Service configuration
Out¶
When you receive a message from the service you must use toOutV1Message()
(see kotlin extension function)
function that returns an instance of 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 one between download and update).
CurrentServiceConfigurationV2¶
CurrentServiceConfigurationV2 has a field called conf
that contains the current service configuration (an instance of UFServiceConfigurationV2
)
UFServiceMessageV1¶
Classes that map states and events of UF Android Client Service
States:¶
Downloading¶
Client is downloading artifacts from server.
Fields:
artifacts
: list of artifacts to download
Artifact¶
Fields:
name
: artifact namesize
: artifact sizemd5
: artifact MD5 checksum
Updating¶
The update process is started. Any request to cancel an update will be rejected.
CancellingUpdate¶
Last update request is being cancelled.
WaitingDownloadAuthorization¶
Waiting authorization to start download.
WaitingUpdateAuthorization¶
Waiting authorization to start update.
WaitingUpdateWindow¶
Waiting for a valid time slot to apply the force update
Idle¶
Client is waiting for new requests from the server.
NoNewState¶
The state in the server is unchanged. NoNewState
is sent only in case of Force Ping.
ConfigurationError¶
Bad service configuration.
Fields:
details
: additional information
Events:¶
Polling¶
Client is contacting server to retrieve new action to execute.
StartDownloadFile¶
A file downloading is started.
Fields:
fileName
: name of the file to download
FileDownloaded¶
A file is downloaded.
Fields:
fileDownloaded
: the file that is downloaded
DownloadProgress¶
Progress of the current file download process.
Progress is sent every minute if at least an additional 10% has been downloaded with respect to previous progress message. Progress is sent for artifacts of any update type.
Fields:
fileName
: the file that is downloadedpercentage
: the percentage downloaded (in [0,1])
AllFilesDownloaded¶
All needed file are downloaded.
UpdateFinished¶
The update is finished.
Fields:
successApply
: true if the update is successfully applied, false otherwise.details
: additional details
Error¶
An error has occurred.
Fields:
details
: additional details
UpdateProgress¶
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 sent only for double-copy system updates.
Fields:
phaseName
: the name of the update phasephaseDescription
: the description of the update phasepercentage
: the progress percentage of the update phase
UpdateAvailable¶
An update is available on cloud.
Fields:
id
: update id
Started¶
The service is started.
Fields:
configuration
: the service configuration
Stopped¶
The service is stopped.
Fields:
configuration
: the service configuration
CantBeStopped¶
The service can't be stopped. It typically happens when an update is currently being installed.
Fields:
configuration
: the service configurationretryIn
: the number of milliseconds to 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