Skip to main content

Ralsina.Me — Roberto Alsina's website

push-docs

Ubuntu Push Client Developer Guide

Version:

0.43+

Introduction

This doc­u­ment de­scribes how to use the Ubun­tu Push Client ser­vice from a plat­form in­te­gra­tor's point of view. Ap­pli­ca­tion de­vel­op­ers are ex­pect­ed to use a much sim­pler API, in turn based on the low­er-lev­el API de­scribed here.

The ex­pect­ed au­di­ence for this doc­u­ment is, there­fore, ei­ther plat­form de­vel­op­er­s, or ap­pli­ca­tion de­vel­op­ers who, for what­ev­er rea­son, can't use or pre­fer not to use the avail­able high­er lev­el APIs.


Let's de­scribe the push sys­tem by way of an ex­am­ple.

Al­ice has writ­ten a chat ap­pli­ca­tion called Chat­ter. Us­ing it, Bob can send mes­sages to Car­ol and vicev­er­sa. Al­ice has a web ap­pli­ca­tion for it, so the way it works now is that Bob con­nects to the ser­vice, posts a mes­sage, and when Car­ol con­nect­s, she gets it. If Car­ol leaves the brows­er win­dow open, it beeps when mes­sages ar­rive.

Now Al­ice wants to cre­ate an Ubun­tu Touch app for Chat­ter, so she im­ple­ments the same ar­chi­tec­ture us­ing a client that does the same thing as the web brows­er. Sad­ly, since ap­pli­ca­tions on Ubun­tu Touch don't run con­tin­u­ous­ly, mes­sages are on­ly de­liv­ered when Car­ol opens the ap­p, and the us­er ex­pe­ri­ence suf­fer­s.

Us­ing the Ubun­tu Push Server, this prob­lem is al­le­vi­at­ed: the Chat­ter serv­er will de­liv­er the mes­sages to the Ubun­tu Push Server, which in turn will send it in an ef­fi­cient man­ner to the Ubun­tu Push Client run­ning in Bob and Car­ol's de­vices. The us­er sees a no­ti­fi­ca­tion (all with­out start­ing the ap­p) and then can launch it if he's in­ter­est­ed in read­ing mes­sages at that point.

Since the app is not start­ed and mes­sages are de­liv­ered opor­tunis­ti­cal­ly, this is both bat­tery and band­width-­ef­fi­cien­t.

IN­SERT DI­A­GRAM HERE

The Ubun­tu Push sys­tem pro­vides:

  • A push serv­er which re­­ceives push mes­sages from the app server­s, queues them and de­liv­ers them ef­­fi­­cien­t­­ly to the de­vices.

  • A push client which re­­ceives those mes­sages, queues mes­sages to the app and dis­­­plays no­ti­­fi­­ca­­tions to the us­er

The full life­cy­cle of a push mes­sage is:

  • Cre­at­ed in a ap­­pli­­ca­­tion-spe­­cif­ic serv­er

  • Sent to the Ubun­­tu Push server, tar­get­ed at a us­er or user+de­vice pair

  • De­liv­ered to one or more Ubun­­tu de­vices

  • Passed through the ap­­pli­­ca­­tion helper for pro­cess­ing

  • No­ti­­fi­­ca­­tion dis­­­played to the us­er (via dif­fer­­ent mech­a­nis­m­s)

  • Ap­­pli­­ca­­tion Mes­sage queued for the ap­p's use

If the us­er in­ter­acts with the no­ti­fi­ca­tion, the ap­pli­ca­tion is launched and should check its queue for mes­sages it has to process.

For the app de­vel­op­er, there are sev­er­al com­po­nents need­ed:

  • A serv­er that sends the push mes­sages to the Ubun­­tu Push serv­er

  • Sup­­port in the client app for reg­is­ter­ing with the Ubun­­tu Push client

  • Sup­­port in the client app to re­act to no­ti­­fi­­ca­­tions dis­­­played to the us­er and process ap­­pli­­ca­­tion mes­sages

  • A helper pro­­gram with ap­­pli­­ca­­tion-spe­­cif­ic knowl­­edge that tran­s­­forms push mes­sages as need­ed.

In the fol­low­ing sec­tion­s, we'll see how to im­ple­ment all the client side part­s. For the ap­pli­ca­tion server, see the Ubun­tu Push Serv­er API sec­tion

The PushNotifications Service

Service:

com.ubun­tu.­Push­No­ti­fi­ca­tions

Object path:

/com/ubun­tu/­Push­No­ti­fi­ca­tion­s/QUOT­ED_P­KG­NAME

The Push­No­ti­fi­ca­tions ser­vice han­dles reg­is­ter­ing the de­vice with the Ubun­tu Push ser­vice to en­able de­liv­ery of mes­sages to it.

Each Ubuntu Touch package has to use a separate object path for security reasons, that's why the object path includes QUOTED_PKGNAME. For example, in the case of the music application, the package name is com.ubuntu.music and QUOTED_PKGNAME is com_2eubuntu_2emusic. Everything that is not a letter or digit has to be quoted as _XX where XX are the hex digits of the character. In practice, this means replacing "." with "_2e" and "-" with "_2f"

com.ubuntu.PushNotifications.Register

string Register(string APP_ID)

Ex­am­ple:

$ gdbus call --session --dest com.ubuntu.PushNotifications --object-path /com/ubuntu/PushNotifications/com_2eubuntu_2emusic \
--method com.ubuntu.PushNotifications.Register com.ubuntu.music_music

('LeA4tRQG9hhEkuhngdouoA==',)

The Reg­is­ter method takes as ar­gu­ment the AP­P_ID (in the ex­am­ple, com.ubun­tu.­mu­sic_­mu­sic) and re­turns a to­ken iden­ti­fy­ing the us­er and de­vice. For this to suc­ceed the us­er must have an Ubun­tu One ac­count con­fig­ured in the de­vice.

The APP_ID is as described in the ApplicationId documentation except that the version is treated as optional. Therefore both com.ubuntu.music_music and com.ubuntu.music_music_1.3.496 are valid.

This to­ken is lat­er used by the ap­pli­ca­tion serv­er to in­di­cate the re­cip­i­ent of no­ti­fi­ca­tion­s. (FIXME: crosslink to serv­er doc)

com.ubuntu.PushNotifications.Unregister

void Unregister(string APP_ID)

Ex­am­ple:

$ gdbus call --session --dest com.ubuntu.PushNotifications --object-path /com/ubuntu/PushNotifications/com_2eubuntu_2emusic \
--method com.ubuntu.PushNotifications.Unregister com.ubuntu.music_music

The Un­reg­is­ter method in­val­i­dates the to­ken ob­tained via Reg­is­ter there­fore dis­abling re­cep­tion of push mes­sages.

The method takes as ar­gu­ment the AP­P_ID (in the ex­am­ple, com.ubun­tu.­mu­sic_­mu­sic) and re­turns noth­ing.

The APP_ID is as described in the ApplicationId documentation except that the version is treated as optional. Therefore both com.ubuntu.music_music and com.ubuntu.music_music_1.3.496 are valid.

The Postal Service

Service:

com.ubun­tu.­Postal

Object path:

/com/ubun­tu/­Postal/QUOT­ED_P­KG­NAME

The Postal ser­vice de­liv­ers the ac­tu­al mes­sages to the ap­pli­ca­tion­s. Af­ter the ap­pli­ca­tion is reg­is­tered, the push client will be­gin de­liv­er­ing mes­sages to the de­vice, which will then (pos­si­bly) cause spe­cif­ic no­ti­fi­ca­tions to be pre­sent­ed to the us­er (mes­sage bub­bles, sound­s, hap­tic feed­bak, etc.) Re­gard­less of whether the us­er ac­knowl­edges those no­ti­fi­ca­tions or not, the pay­load of the push mes­sage is put in the Postal ser­vice for the ap­pli­ca­tion to pick up.

Be­cause us­er re­sponse to no­ti­fi­ca­tions can cause ap­pli­ca­tion ac­ti­va­tion, apps should check the sta­tus of the Postal ser­vice ev­ery time the ap­pli­ca­tion ac­ti­vates

com.ubuntu.Postal.Post

void Post(string APP_ID, string message)

Ex­am­ple:

gdbus call --session --dest com.ubuntu.Postal --object-path /com/ubuntu/Postal/com_2eubuntu_2emusic \
--method com.ubuntu.Postal.Post com.ubuntu.music_music \
'"{\"message\": \"foobar\", \"notification\":{\"card\": {\"summary\": \"yes\", \"body\": \"hello\", \"popup\": true, \"persist\": true}}}"'

The ar­gu­ments for the Post method are AP­P_ID (in the ex­am­ple, com.ubun­tu.­mu­sic_­mu­sic) and a JSON string de­scrib­ing a push mes­sage.

De­pend­ing on the con­tents of the push mes­sage it may trig­ger user-­fac­ing no­ti­fi­ca­tion­s, and will queue a mes­sage for the app to get via the PopAll method.

The APP_ID is as described in the ApplicationId documentation except that the version is treated as optional. Therefore both com.ubuntu.music_music and com.ubuntu.music_music_1.3.496 are valid.

com.ubuntu.Postal.PopAll

array{string} PopAll(string APP_ID)

Ex­am­ple:

$ gdbus call --session --dest com.ubuntu.Postal --object-path /com/ubuntu/Postal/com_2eubuntu_2emusic \
--method com.ubuntu.Postal.PopAll com.ubuntu.music_music

(['{"message": "foobar", "notification":{"card": {"summary": "yes", "body": "hello", "popup": true, "persist": true}}}'],)

The ar­gu­ment for the PopAll method is the AP­P_ID and it re­turns a list of strings, each string be­ing a sep­a­rate push mes­sage re­ceived by the Ubun­tu Push Clien­t, ei­ther via Post or from the Ubun­tu Push ser­vice.

The in­ter­est­ing part of the push mes­sages for the app is prob­a­bly the "mes­sage" el­e­men­t, but the whole struc­ture is made avail­able.

Post Signal

void Post(string APP_ID)

Ev­ery time a no­ti­fi­ca­tion is post­ed, the postal ser­vice will emit the Post sig­nal. Your app can con­nect to it to re­act to in­com­ing no­ti­fi­ca­tions if it's run­ning when they ar­rive. Re­mem­ber that on Ubun­tu Touch, the ap­pli­ca­tion life­cy­cle means it will of­ten not be run­ning when no­ti­fi­ca­tions ar­rive.

The ob­ject path is sim­i­lar to that of the Postal ser­vice meth­od­s, con­tain­ing the QUOT­ED_P­KG­NAME.

Application Helpers

The pay­load de­liv­ered to push-­client will be passed on­to a helper pro­gram that can mod­i­fy it as need­ed be­fore pass­ing it on­to the postal ser­vice.

The helper receives two arguments infile and outfile. The message is delivered via infile and the transformed version is placed in outfile.

This is the sim­plest pos­si­ble use­ful helper, which sim­ply pass­es the mes­sage through un­changed:

#/bin/sh
cat $1 > $2

Helpers need to be added to the click pack­age man­i­fest:

{
        "name": "com.ubuntu.developer.ralsina.hello",
        "description": "description of hello",
        "framework": "ubuntu-sdk-14.10-qml-dev2",
        "architecture": "all",
        "title": "hello",
        "hooks": {
                "hello": {
                        "apparmor": "hello.json",
                        "desktop": "hello.desktop"
                },
                "helloHelper": {
                        "apparmor": "helloHelper-apparmor.json",
                        "push-helper": "helloHelper.json"
                }
        },
        "version": "0.2",
        "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>"
}

Here, we cre­at­ed a hel­loHelper en­try in hooks that has an ap­par­mor pro­file and an ad­di­tion­al JSON file for the push-helper hook.

hel­loHelper-ap­par­mor.j­son must con­tain on­ly the push-no­ti­fi­ca­tion-­client pol­i­cy group:

{
        "policy_groups": [
                "push-notification-client"
        ],
        "policy_version": 1.2
}

And hel­loHelper.j­son must have at least a ex­ec key with the path to the helper ex­e­cutable rel­a­tive to the json, and op­tion­al­ly an ap­p_id key con­tain­ing the short id of one of the apps in the pack­age (in the for­mat pack­a­ge­name_app­name with­out a ver­sion):

{
        "exec": "helloHelper",
        "app_id": "com.ubuntu.developer.ralsina.hello_hello"
}

Push Message Format

The Ubun­tu Push Client re­ceives push mes­sages from dif­fer­ent sources, in­clud­ing ap­pli­ca­tions in the de­vice (via Post) and ap­pli­ca­tion servers (via the Ubun­tu Push server). Those push mes­sages are de­scribed us­ing JSON strings. This sec­tion de­scribes the parts rel­e­vant to clien­t-­side app­s.

Here's a sim­ple ex­am­ple:

{
        "message": "foobar",
        "notification": {
                "card": {
                        "summary": "yes",
                        "body": "hello",
                        "popup": true,
                        "persist": true
                }
                "sound": "buzz.mp3",
                "vibrate": {
                        "duration": 200,
                        "pattern": (200, 100),
                        "repeat": 2
                }
                "emblem-counter": {
                        "count": 12,
                        "visible": true
                }
        }
}
message:

A JSON ob­ject that is passed as-is to the ap­pli­ca­tion.

notification:

(op­tion­al) De­scribes the user-­fac­ing no­ti­fi­ca­tions trig­gered by this push mes­sage.

The no­ti­fi­ca­tion can con­tain one card. Each card de­scribes a spe­cif­ic no­ti­fi­ca­tion to be giv­en to the user, and has the fol­low­ing field­s:

summary:

(re­quired) a ti­tle. The card will not be pre­sent­ed if this is miss­ing.

body:

longer tex­t, de­faults to emp­ty.

actions:

If emp­ty (the de­fault­), a bub­ble no­ti­fi­ca­tion is non-click­able. More en­tries change it to be click­able and (for bub­bles) turn it in­to snap-de­ci­sion­s. FIXME this needs re­word­ing, links to de­scrip­tions of snap de­ci­sions in the SD­K, etc

icon:

An icon re­lat­ing to the event be­ing no­ti­fied. De­faults to emp­ty (no icon); a sec­ondary icon re­lat­ing to the ap­pli­ca­tion will be shown as well, re­gard­less of this field.

timestamp:

Sec­onds since the unix epoch, on­ly used for per­sist (for now)

persist:

Whether to show in no­ti­fi­ca­tion cen­tre; de­faults to false

popup:

Whether to show in a bub­ble. Users can dis­able this, and can eas­i­ly miss them, so don't re­ly on it ex­clu­sive­ly. De­faults to false.

The no­ti­fi­ca­tion can con­tain a sound field. This is the path to a sound file. The us­er can dis­able it, so don't re­ly on it ex­clu­sive­ly. De­faults to emp­ty (no sound). This is a rel­a­tive path, and will be looked up in (a) the ap­pli­ca­tion's .lo­cal/share/<p­kg­name>, and (b) stan­dard xdg dirs.

The no­ti­fi­ca­tion can con­tain a vi­brate field, caus­ing hap­tic feed­back, that has the fol­low­ing con­tent:

duration:

du­ra­tion in mil­lisec­onds

pattern:

a list of in­te­gers de­scrib­ing a vi­bra­tion pat­tern

repeat:

num­ber of times the pat­tern has to be re­peat­ed (de­faults to 1, 0 is the same as 1)

The no­ti­fi­ca­tion can con­tain a em­blem-­counter field, with the fol­low­ing con­tent:

count:

a num­ber to be dis­played over the ap­pli­ca­tion's icon in the launch­er.

visible:

set to true to show the coun­ter, or false to hide it.

The ac­tions this trig­gers can in­clude:


FIXME crosslink to hel­lo ex­am­ple app on each method

FIXME Doc­u­ment ac­tions (when us­able)


Security

To use the push API, ap­pli­ca­tions need to re­quest per­mis­sion in their se­cu­ri­ty pro­file, us­ing some­thing like this:

{
        "policy_groups": [
                "networking",
                "push-notification-client"
        ],
        "policy_version": 1.2
}

Ubuntu Push Server API

The Ubuntu Push server is located at https://push.ubuntu.com and has a single endpoint: /notify. To notify a user, your application has to do a POST with Con­tent-­type: ap­pli­ca­tion/j­son.

Here is an ex­am­ple of the POST body us­ing all the field­s:

{
        "appid": "com.ubuntu.music_music",
        "expire_on": "2014-10-08T14:48:00.000Z",
        "token": "LeA4tRQG9hhEkuhngdouoA==",
        "clean_pending": true,
        "replace_tag": "tagname",
        "data": {
                "message": "foobar",
                "notification": {
                        "card": {
                                "summary": "yes",
                                "body": "hello",
                                "popup": true,
                                "persist": true
                        }
                        "sound": "buzz.mp3",
                        "vibrate": {
                                "duration": 200,
                                "pattern": (200, 100),
                                "repeat": 2
                        }
                        "emblem-counter": {
                                "count": 12,
                                "visible": true
                        }
                }
        }
}
appid:

ID of the ap­pli­ca­tion that will re­ceive the no­ti­fi­ca­tion, as de­scribed in the client side doc­u­men­ta­tion.

expire_on:

Ex­pi­ra­tion date/­time for this mes­sage, in ISO8601 Ex­ten­dend for­mat

token:

The to­ken iden­ti­fy­ing the user+de­vice to which the mes­sage is di­rect­ed, as de­scribed in the client side doc­u­men­ta­tion.

clear_pending:

Dis­cards all pre­vi­ous pend­ing no­ti­fi­ca­tion­s.

replace_tag:

If there's a pend­ing no­ti­fi­ca­tion with the same tag, delete it be­fore queu­ing this new one.

data:

A JSON ob­jec­t.

In this ex­am­ple, da­ta is a push mes­sage but that's not nec­es­sar­i­ly the case. The con­tent of the da­ta field will be passed to the helper ap­pli­ca­tion which has to pro­duce a push mes­sage.

{
        "appid": "com.ubuntu.music_music",
        "token": "RAlG3ltQqK0gjGcU0QTUHw==",
        "data": {
                "message": {
                        "text": "hello"
                },
                "notification": {
                        "card": {
                                "summary": "the summary",
                                "body": "hello",
                                "popup": true,
                                "persist": true
                        }
                }
        }
}

Contents © 2000-2023 Roberto Alsina