UnifiedPush
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Android

UnifiedPush Spec: AND_3.0.0

Index

Resources

  • All extras typed String MUST be UTF-8 encoded.
  • All required extras MUST be non-null.
  • Any request containing an extra that do not follow the specification MUST be ignored.
  • End user application: This is the application that will receive notifications with UnifiedPush; All the logic required for the end user application can be done with a library.
  • Connection Token: This is a randomly generated token to identify the registration from the connector and the distributor. This token is generated by the connector, and send for the first time during the registration intent (org.unifiedpush.android.distributor.REGISTER). This token must contain sufficient entropy so it cannot be guessed. To generate this token, UUIDv4 (RFC9562) is suggested. It is unique on distributor and connector side. Every requests to the connector contains this token. If the connector doesn’t know this token, the request is ignored. This is a string of maximum 100 bytes.
  • VAPID public key: This is a public key on the P-256 curve encoded in the uncompressed form SEC 1 (section 2.3.3, replicated from X9.62), and base64url encoded RFC7515. This public key is generated by the end user application, and send during the registration intent (org.unifiedpush.android.distributor.REGISTER). It is used by the application server to identify itself to the push server, following the RFC8292. Some distributors MAY require a valid VAPID key, and response with a registration fail if not present (org.unifiedpush.android.connector.REGISTRATION_FAILED). This is a 87 bytes long string.
  • Message Id: This is an id to identify a message from the distributor to the connector. If present, the connector must acknowledge the message to the distributor (with org.unifiedpush.android.distributor.MESSAGE_ACK). This is a string of maximum 100 bytes. To prevent an application to acknowledge another application’s message, the distributor SHOULD either:
    • Use unique token which contain sufficient entropy so it cannot be guessed; UUIDv4 (RFC9562) is suggested.
    • Save this id linked to the connection token, so a same id could be send to 2 different applications but one cannot acknowledge for the other.
  • Push message: This is an array of bytes (ByteArray) sent by the application server to the push server. The distributor sends this message to the end user application. It MUST be the raw POST data received by the push server (or the rewrite proxy if present). The message MUST be an encrypted content that follows RFC8291. Its size is between 1 and 4096 bytes (inclusive).
  • Endpoint: This is the URL of the push resource as defined by RFC8030. This url point to the push server and is distributed to the end user application by the distributor. This MUST be at most 1000 bytes. As defined by RFC8030, authorization is managed using capability URLs (CAP-URI). Therefore, the endpoint MUST contain enough random entropy to ensure it is difficult to successfully guess a valid URL. We recommend using a 160 bits (20 bytes) random value URL-safe base64 encoded string.
  • Short description of the registration: This is a string send by the end user application during registration describing the registration purpose (eg. the account name of the application) the distributor may show on its user interface. It is at most 100 bytes long string.

Overview

Bellow, some usual events of the lifecycle of a connection between the end user application and the distributor:

AS: the application server C: the end user application (with a connector) D: the distributor PS: the push server (connected to D)

  1. C enables UnifiedPush for the first time, try to select the default distributor.
    1. C: Request the default application for the deep link unifiedpush://link
    2. D: Link Activity opens and set result to RESULT_OK with a pending intent (extra: pi)
  2. C request one or more registration, the token is different for each registration.
    1. C->D: org.unifiedpush.android.distributor.REGISTER SDK<24: (extra: token, pi) SDK>=24: (extra: token, flag: FLAG_SHARE_IDENTITY), optionally: (extra: vapid, message)
    2. C<-D: org.unifiedpush.android.connector.NEW_ENDPOINT (extra: token, endpoint, id)
    3. C->D: org.unifiedpush.android.distributor.MESSAGE_ACK (extra: token, id)
    4. C sends the endpoint to AS
  3. From time to time, like every time the application starts, C register to D to avoid inconsistent state. C<->D follows the same steps under point 2., using the already used token.
  4. AS sends a push message
    1. AS sends webpush request to PS, and PS follows to D
    2. C<-D: org.unifiedpush.android.connector.MESSAGE (extra: token, bytesMessage, id)
    3. C->D: org.unifiedpush.android.distributor.MESSAGE_ACK (extra: token, id)
  5. C unregister a registration
    1. C->D: org.unifiedpush.android.distributor.UNREGISTER (extra: token)
    2. C removes the token, may put it in waitlist to check unregistration status
    3. C<-D: org.unifiedpush.android.connector.UNREGISTERED (extra: token)
  6. D. checks a registration with C. that hasn’t be used for weeks (~ping):
    1. C<-D: org.unifiedpush.android.connector.NEW_ENDPOINT (extra: token, endpoint, id)
    2. C->D: org.unifiedpush.android.distributor.MESSAGE_ACK (extra: token, id)
  7. C. hasn’t acknowledge any message nor ping (point 5.) since 30 days, or the user logout of its distributor, D unregister the registration:
    1. C<-D: org.unifiedpush.android.connector.UNREGISTERED (extra: token)
  8. C. tries to register but the registration fail
    1. C->D: org.unifiedpush.android.distributor.REGISTER SDK<24: (extra: token, pi) SDK>=24: (extra: token, flag: FLAG_SHARE_IDENTITY), optionally: (extra: vapid, message)
    2. C<-D: org.unifiedpush.android.connector.REGISTRATION_FAILED (extra: token, reason)
    3. reason may be:
      1. “INTERNAL_ERROR”: C can try again directly to register (point 2.)
      2. “NETWORK”: C waits for the network to be back and try again to register (point 2.)
      3. “ACTION_REQUIRED”: D is waiting for a user interaction. It can show a notification to inform the user. C can try again to register (point 2.) when the user is back on C.
      4. “VAPID_REQUIRED”: If C supports VAPID, it can retry directly to register with a VAPID key (point 2.), else C inform the user they can’t use this disributor because it requires a server feature the application doesn’t support.

Push Distributor

The push distributor MUST expose the Registration Broadcast Receiver, allowing end user applications to register for push related messages. It also MUST expose an activity that can be requested by end user applications using the deep link unifiedpush://link; It allows user to set a default distributor and to use the system UI to select the distributor.

Distributor manifest

The push distributor MUST expose a broadcast receiver with the following actions:

This broadcast receiver is the Registration Broadcast Receiver.

The push distributor MUST also expose an activity that can be requested with the deep link unifiedpush://link.

End User Application

The end user application MUST expose the Messaging Broadcast Receiver, allowing the distributor to send push related messages. It also MUST expose a service the distributor MAY bind to to raise the application to the foreground.

End User Application Manifest

The end user application MUST expose a broadcast receiver with the following actions:

This broadcast receiver is the Messaging Broadcast Receiver.

The end user application MUST expose a Service to raise to the foreground with the following action:

Registration Broadcast Receiver

The exposed broadcast receiver of the push distributor MUST handle 3 different actions:

There is a 4th action the distributor SHOULD handle:

org.unifiedpush.android.distributor.REGISTER

The connector sends this action to register to push messages. The intent MUST contain the following extra:

  • token (String): this is the connection token as defined in the Resources. This is where a new token is used for the first time.

If the application runs on SDK 34 or above, the broadcast message MUST be send with broadcast option flag FLAG_SHARE_IDENTITY. Else, the intent MUST contain the following extra:

  • pi (PendingIntent): an IMMUTABLE pending intent requesting a broadcast to a dummy application (org.unifiedpush.dummy_app). This pending intent is used to get the package name of the end user application.

Therefore, a distributor installed on a device that supports application targeting SDK lower than 34 MUST implement the resolution using the PendingIntent. When receiving a broadcast without shared identify, with PendingIntent, the distributor SHOULD check that the requesting application targets a SDK lower than 34.

The distributor MUST be able to handle many registrations with a single application.

It MAY be sent with one or more of the following 2 extras:

  • message (String): this is the short description of the registration as defined in the Resources, that the distributor MAY show to the user.
  • vapid (String, 87 bytes): a VAPID public key as defined in the Resources. Some distributors MAY require a valid VAPID key, and response with org.unifiedpush.android.connector.REGISTRATION_FAILED if it is not present.

The distributor MUST send a broadcast intent to one of the following actions when it handles this action:

The distributor SHOULD NOT create a new endpoint if a valid registration exist for the token and nothing has been updated.

The distributor MAY limit the number of allowed registrations per application. This limit SHOULD be over 1000. If the limit is reached, the distributor should inform the user and the next registration fail.

org.unifiedpush.android.distributor.UNREGISTER

The connector sends this action to unregister from push messages. The intent MUST contain 1 extra:

  • token (String): This is the connection token as defined in the Resources supplied by the end user application during registration. If this token is not known by the distributor, the distributor will ignore this request.

If the application runs on SDK 34 or above, the broadcast message MUST be send with broadcast option flag FLAG_SHARE_IDENTITY. Else, the intent MUST contain the following extra:

  • pi (PendingIntent): an IMMUTABLE pending intent requesting a broadcast to a dummy application (org.unifiedpush.dummy_app). This pending intent is used to get the package name of the end user application.

A distributor MAY ignore the shared identity or the pending intent to identify the end user application, if it has linked the identity of the application to the token during the registration.

Therefore, a distributor relying on the identity shared during this intent, installed on a device that supports application targeting SDK lower than 34 MUST implement the resolution using the PendingIntent. When receiving a broadcast without shared identify, with PendingIntent, the distributor SHOULD check that the requesting application targets a SDK lower than 34.

After sending this action, the end user application MUST remove the connection token from the valid tokens even if the unregistration is not acknowledged by the distributor with org.unifiedpush.android.connector.UNREGISTERED. The end user application MAY cache this connection token to inform the user if the unregistration is not acknowledge after some time.

The distributor MUST send org.unifiedpush.android.connector.UNREGISTERED when the unregistration is processed.

org.unifiedpush.android.distributor.MESSAGE_ACK

Whenever the connector receives a message with the extra id it MUST reply with this action to the distributor to acknowledge the message’s reception.

The intent MUST contain 2 extras:

  • token (String): This is the connection token as defined in the Resources supplied by the end user application during registration. If this token is not known by the distributor, the distributor will ignore this request.
  • id (String, max 100 bytes): This is the message id as defined in the Resources.

A distributor MAY retry to send a message that has not been acknowledged.

The distributor exposes an activity the end user application can request using the deep link unifiedpush://link.

This allows user to set a default distributor and to use the system UI to select the distributor. End user applications SHOULD use this method to link to a distributor when selecting a distributor for the first time, for instance when the user login to the application, or when UnifiedPush is enabled.

End user applications MUST NOT rely on the default application opening the deep link when the users want to select manually their distributor.

This activity MUST have an intent filter for incoming links with the following features:

  • The intent action is android.intent.action.VIEW
  • It includes the android.intent.category.DEFAULT category
  • It contains the data tag for unifiedpush scheme and link host

When the activity is created, it can retrieve the end user application packageName using the Activity.callingPackage method.

If the intent’s data and the callingPackage exist, the result is set to RESULT_OK (-1) with a pending intent in the extra:

  • pi (PendingIntent): an IMMUTABLE pending intent requesting a broadcast to a dummy application (org.unifiedpush.dummy_app). This pending intent is used to get the package name of the distributor.

Else, the result is set to RESULT_CANCELED.

The activity is finished right after the result is set.

The end user application requesting this activity MUST expect a result, for instance using startActivityForResult

Messaging Broadcast Receiver

The exposed broadcast receiver of the end user application MUST handle 3 different actions:

There are 2 additional actions the connector SHOULD handle:

org.unifiedpush.android.connector.NEW_ENDPOINT

The distributor MUST send this action to the registered application in the following cases:

The intent MUST contain the following 2 extras:

  • token (String): This is the connection token as defined in the Resources supplied by the end user application during registration. If this token is not known by the connector, the connector will ignore this request.
  • endpoint (String): the endpoint URL as defined in the Resources.

It SHOULD be sent with the following extra:

A distributor MAY remove endpoints that haven’t receive any org.unifiedpush.android.distributor.MESSAGE_ACK within 30 seconds after the new endpoint intent. If the distributor removes the endpoint, it MUST send org.unifiedpush.android.connector.UNREGISTERED to the end user application.

org.unifiedpush.android.connector.REGISTRATION_FAILED

The distributor MUST send this action to the registered application if:

  • the token is already registered for another application
  • the registration can not be processed (for instance when the distributor is not connected to its server)
  • a requested feature is not supported by the distributor

The action MUST contain the following extra:

  • token (String): This is the connection token as defined in the Resources supplied by the end user application during registration. If this token is not known by the connector, the connector will ignore this request.

The intent SHOULD contain 1 additional extra:

  • reason (String): the type of error causing the registration fail.

The reason MUST be either:

  • “INTERNAL_ERROR”: This is a generic error type, the connector can try again directly.
  • “NETWORK”: The registration failed because of missing network connection, try again when network is back. A distributor can also return this reason if the endpoint for a registration may have changed and may be invalidated but the distributor can’t know because of missing network connection.
  • “ACTION_REQUIRED”: The distributor requires a user action to work. For instance, the distributor may be log out of the push server and requires the user to log in. If the distributor has a limit of number of registrations and this limit has been reached, the distributor sends this reason. The distributor MUST send this reason if the direct sending of a new registration will fail and this is not due to a network problem.
  • “VAPID_REQUIRED”: If the distributor requires a VAPID key and the end user application doesn’t send one, the distributor respond with this reason.

The connector MUST change the registration token (received with this action) for the next registration.

If a connector receives this action after it already received a NEW_ENDPOINT action for the same token then it MUST ignore this action.

org.unifiedpush.android.connector.MESSAGE

The distributor MUST send this action to the registered application to forward a push message received on the push server to the end user application.

The distributor MUST send the following 2 extras:

  • token (String): This is the connection token as defined in the Resources supplied by the end user application during registration. If this token is not known by the connector, the connector will ignore this request.
  • bytesMessage (ByteArray): This is the push message as defined in the Resources.

It SHOULD be sent with the following extra:

The distributor SHOULD follow the push message urgency as defined in RFC8030, section 5.3. If the push server does not send an urgency header, the urgency is considered as normal. Else, only messages more urgent than the minimum urgency SHOULD be send to the end user application. The minimum urgency depending on the device state is as follow, in order of increasing urgency:

Urgency Device State Example Application
very-low On power and Wi-Fi Advertisements
low On either power or Wi-Fi Topic updates
normal On neither power nor Wi-Fi Chat or Calendar Message
high Low battery Incoming phone call or time-sensitive alert

The distrbutor MUST raise the application to the foreground importance for 5 seconds when sending this intent. There is different way to achieve this:

  • By using privileged functions if the distributor is installed as a System application. For instance:
    • By sending the broadcast intent with the broadcastOption setTemporaryAppAllowlist
    • By calling the PowerExemptionManager’s addToTemporaryAllowList
  • By binding with foreground importance to the Service to raise to the foreground exposed by the end user application

org.unifiedpush.android.connector.UNREGISTERED

The distributor MUST send this action to the registered application to inform it about unregistration.

The intent MUST have the following extra:

  • token (String): This is the connection token as defined in the Resources supplied by the end user application during registration. If this token is not known by the connector, the connector will ignore this request.

This is send when:

Service to raise to the foreground

The end user application MUST expose a service with the following action:

The distributor MAY bind to this service during 5 seconds when sending a message to raise the application to the foreground. This allows the application to start a foreground service from the background without unrestricted battery usage rights.

References

Internal References

Resources

Registration Broadcast Receiver

Link Activity

Messaging Broadcast Receiver

Service to raise to the foreground

org.unifiedpush.android.distributor.REGISTER

org.unifiedpush.android.distributor.UNREGISTER

org.unifiedpush.android.distributor.MESSAGE_ACK

org.unifiedpush.android.connector.NEW_ENDPOINT

org.unifiedpush.android.connector.REGISTRATION_FAILED

org.unifiedpush.android.connector.MESSAGE

org.unifiedpush.android.connector.UNREGISTERED

Normative References

CAP-URI Capability URLs

SEC 1 SEC 1: Elliptic Curve Cryptography

RFC7515 JSON Web Signature (JWS)

RFC9562 Universally Unique IDentifiers (UUIDs)

RFC8030 Generic Event Delivery Using HTTP Push

RFC8292 Voluntary Application Server Identification (VAPID) for Web Push

RFC8291 Message Encryption for Web Push