Skip to content

Latest commit

 

History

History
1166 lines (617 loc) · 28 KB

sc_push.md

File metadata and controls

1166 lines (617 loc) · 28 KB

Module sc_push

This module is the Erlang API for Silent Circle push notifications and registrations.

Authors: Edwin Fine (efine@silentcircle.com).

Description

The notification format is very flexible. There is a simple format and a more elaborate, canonical format.

The simple format requires push tokens to have been registered using the registration API, so that the push tag can be used as a destination for thte notification.

The canonical format requires a list of receivers to be provided for each notification. This allows a notification to be sent to multiple recipients on different services, specifying the destination in a number of possible formats.

   [{alert, <<"Hello there.">>},
    {tag, <<"foo@example.com">>}]

The canonical format is as follows. Any or all of the receiver types may be specified, but at least one, for example svc_tok, must be provided.

   [{alert, <<"Hello there.">>},
    {receivers, [{tag, [Tag]},
                 {svc_tok, [{Service, Token}]},
                 {svc_appid_tok, [{Service, AppId, Token}]},
                 {device_id, [DeviceId]}]}]

More formally, the notification specification is defined in notification().

   AppId = <<"com.example.MyApp">>,
   Tok = <<"4df5676af4307b3e0366da63e8854752d9219d8b9848f7c31bbab8741730fda6">>,
   [{alert,<<"Hello">>},
    {aps,[{badge,22}]},
    {receivers,[{svc_appid_tok,[{apns, AppId, Tok}]}]}]

Data Types


alert() = binary()

Alert message as binary string.


alert_spec() = {alert, alert()}

app_id() = binary()

AppId as binary string, e.g. <<"com.foo.app">>.


async_send_result() = {ok, {async_status(), uuid()}} | {error, term()}

async_send_results() = [async_send_result()]

async_status() = submitted | queued

child_id() = term()

Not a pid().


device_id() = binary()

device_id_spec() = {device_id, device_ids()}

device_ids() = [device_id()]

dist() = binary()

Distribution, e.g. <<"prod">>, <<"dev">>.


filtered_result() = {reg_info_list(), reg_err_list()}

mode() = sync | async

receiver_regs() = filtered_result()

receiver_specs() = [receiver_spec()]

receivers() = {receivers, receiver_specs()}

reg_api_func_name() = get_registration_info_by_device_id | get_registration_info_by_svc_tok | get_registration_info_by_tag

reg_err() = {error, reg_err_term()}

reg_err_list() = [reg_err_term()]

reg_err_term() = term()

reg_id_keys() = [sc_push_reg_db:reg_id_key()]

reg_info() = [reg_info_prop()]

reg_info_list() = [reg_info()]

reg_info_prop() = {service, service()} | {token, token()} | {app_id, app_id()} | {dist, dist()}

reg_ok() = {ok, reg_info_list()}

reg_result() = reg_ok() | reg_err()

reg_results() = [reg_result()]

service() = atom()

Service type, e.g. apns, gcm


service_specific_spec() = {service(), std_proplist()}

std_proplist() = sc_types:proplist(atom(), term())

svc_appid_tok() = {service(), app_id(), token()}

svc_appid_tok_spec() = {svc_appid_tok, svc_appid_toks()}

svc_appid_toks() = [svc_appid_tok()]

svc_tok() = {service(), token()}

svc_tok_spec() = {svc_tok, svc_toks()}

svc_toks() = [svc_tok()]

sync_send_result() = {ok, {uuid(), props()}} | {error, term()}

sync_send_results() = [sync_send_result()]

tag() = binary()

Opaque group identifier.


tag_spec() = {tag, tag()}

tag_spec_mult() = {tag, tags()}

tags() = [tag()]

token() = binary()

APNS token as hex string, GCM registration id.


uuid() = binary()

A raw uuid in the form <<_:128>>.

Function Index

async_send/1Asynchronously sends a notification specified by proplist Notification.
async_send/2
deregister_device_ids/1Deregister multiple registered device IDs.
deregister_id/1Deregister a registered ID.
deregister_ids/1Deregister multiple registered IDs.
get_all_service_configs/0Get all service configurations.
get_registration_info_by_id/1Get registration info corresponding to a device ID.
get_registration_info_by_svc_tok/2Get registration info corresponding to service and reg id.
get_registration_info_by_tag/1Get registration info corresponding to a tag.
get_service_config/1Get service configuration.
get_session_pid/1Get pid of named session.
get_version/0Get scpf version string as a binary.
make_service_child_spec/1Make a supervisor child spec for a service.
quiesce_all_services/0Quiesce all services.
quiesce_service/1Quiesce a service.
quiesce_session/2Quiesce session.
register_id/1 Register to receive push notifications.
register_ids/1Perform multiple registrations.
register_service/1Register a service in the service configuration registry.
send/1Send a notification specified by proplist Notification.
send/2
start/0
start_service/1Start a push service.
start_session/2Start named session as described in the options proplist Opts.
stop_service/1Stops a service and all sessions for that service.
stop_session/2Stop named session.

Function Details

async_send/1


async_send(Notification) -> Result

Asynchronously sends a notification specified by proplist Notification. The contents of the proplist differ depending on the push service used (see notification()).

The asynchronous response is sent to the calling pid's mailbox as a tuple. The tuple is defined asasync_message() as shown below.

  -type svc_response_id() :: apns_response | gcm_response | atom().
  -type version() :: atom(). % For example, `` 'v1' ''.
  -type async_message() :: {svc_response_id(), version(), gen_send_result()}.

async_send/2


async_send(Notification, Opts) -> Result

deregister_device_ids/1


deregister_device_ids(IDs::[binary()]) -> ok | {error, term()}

Deregister multiple registered device IDs.

deregister_id/1


deregister_id(ID::sc_push_reg_db:reg_id_key()) -> ok | {error, term()}

Deregister a registered ID.

deregister_ids/1


deregister_ids(IDs::reg_id_keys()) -> ok | {error, term()}

Deregister multiple registered IDs.

get_all_service_configs/0


get_all_service_configs() -> SvcConfigs

Get all service configurations

get_registration_info_by_id/1


get_registration_info_by_id(ID::sc_push_reg_db:reg_id_key()) -> notfound | [sc_types:reg_proplist()]

Get registration info corresponding to a device ID. Note that multiple results should not be returned, although the returned value is a list for consistency with the other APIs.

get_registration_info_by_svc_tok/2


get_registration_info_by_svc_tok(Service::string() | atom(), RegId::binary() | string()) -> notfound | sc_types:reg_proplist()

Get registration info corresponding to service and reg id.

get_registration_info_by_tag/1


get_registration_info_by_tag(Tag::binary()) -> notfound | [sc_types:reg_proplist()]

Get registration info corresponding to a tag. Note that multiple results may be returned if multiple registrations are found for the same tag.

get_service_config/1


get_service_config(Service) -> Result

Get service configuration

See also: start_service/1.

get_session_pid/1


get_session_pid(Name) -> Result
  • Name = atom()
  • Result = pid() | undefined

Get pid of named session.

get_version/0


get_version() -> Result
  • Result = binary()

Get scpf version string as a binary. In the unlikely event that an error occurs, the version will be returned as <<"?.?.?">>.

make_service_child_spec/1


make_service_child_spec(Opts) -> Result

Make a supervisor child spec for a service. The name of the supervisor module is obtained from the API module, and is what will be started. Obviously, it must be a supervisor.

See also: start_service/1.

quiesce_all_services/0


quiesce_all_services() -> ok

Quiesce all services.

See also: quiesce_service/1.

quiesce_service/1


quiesce_service(Service) -> Result
  • Service = atom() | pid()
  • Result = ok | {error, term()}

Quiesce a service. This instructs the service not to accept any more service requests to allow pending requests to drain. This call does not return until all pending service requests have completed.

quiesce_session/2


quiesce_session(Service, Name) -> Result
  • Service = atom()
  • Name = atom()
  • Result = ok | {error, Reason}
  • Reason = term()

Quiesce session. This puts the session in a state that rejects new push requests, but continues to service in-flight requests. Once there are no longer any in-flight requests, the session is stopped.

register_id/1


Register to receive push notifications.

Props is a proplist with the following elements, all of which are required:

{service, string()}
A name that denotes the push service for which the caller is registering. Currently this includes apns for Apple Push, and gcm for Android (Google Cloud Messaging) push.
{token, string()}
The "token" identifying the destination for a push notification - for example, the APNS push token or the GCM registration ID.
{tag, string()}
The identification for this registration. Need not be unique, but note that multiple entries with the same tag will all receive push notifications. This is one way to support pushing to a user with multiple devices.
{device_id, string()}
The device identification for this registration. MUST be unique, so a UUID is recommended.
{app_id, string()}
The application ID for this registration, e.g. com.example.MyApp. The exact format of application ID varies between services.
{dist, string()}
The distribution for this registration. This optional value must be either "prod" or "dev". If omitted, "prod" is assumed. This affects how pushes are sent, and behaves differently for each push service. For example, in APNS, this will select between using production or development push certificates. It currently has no effect on Android push, but it is likely that this may change to select between using "production" or "development" push servers.

register_ids/1


register_ids(ListOfProplists::[sc_types:reg_proplist()]) -> ok | {error, term()}

Perform multiple registrations. This takes a list of proplists, where the proplist is defined in register_id/1.

See also: register_id/1.

register_service/1


register_service(Svc) -> ok

Register a service in the service configuration registry.

See also: start_service/1.

send/1


send(Notification) -> Result

Send a notification specified by proplist Notification. The contents of the proplist differ depending on the push service used.

Notifications have generic and specific sections. The specific sections are for supported push services, and contain options that only make sense to the service in question.

Providing a UUID as shown is not required, but is recommended for tracking purposes.

  Notification = [
      {uuid, <<"4091b3a2-df6a-443e-a119-8f1d430ed53c">>},
      {alert, <<"Notification to be sent">>},
      {tag, <<"user@domain.com">>},
      % ... other generic options ...
      {aps, [APSOpts]},
      {gcm, [GCMOpts]},
      {etc, [FutureOpts]} % Obviously etc is not a real service.
  ].

See also: sync_send_results().

send/2


send(Notification, Opts) -> Result

start/0

start() -> any()

start_service/1


start_service(ServiceOpts) -> Result

Start a push service.

  Cfg = [
      {name, 'null'},
      {mod, 'sc_push_svc_null'},
      {description, "Null Push Service"},
      {sessions, [
              [
                  {name, 'null-com.silentcircle.SCPushSUITETest'},
                  {mod, sc_push_svc_null},
                  {config, []}
              ]
          ]}
  ],
  {ok, Pid} = sc_push:start_service(Cfg).
{name, atom()}
A name that denotes the push service for which the caller is registering. Currently this includes apns for Apple Push, and gcm for Android (Google Cloud Messaging) push. There is also a built-in null service for testing.
{mod, atom()}
All calls to a service are done via the service-specific API module, which must be on the code path. This is the name of that API module. All API modules have the same public interface and are top-level supervisors. They must therefore implement the Erlang supervisor behavior.
{description, string()}
A human-readable description of the push service.
{sessions, list()}
A list of proplists. Each proplist describes a session to be started. See start_session/1 for details.

start_session/2


start_session(Service, Opts) -> Result
  • Service = atom()
  • Opts = std_proplist()
  • Result = {ok, Pid} | {error, Reason}
  • Pid = pid()
  • Reason = term()

Start named session as described in the options proplist Opts. config is service-dependent.

name must be in the format service-api_key. For example, if the service name is apns, and the api_key is, in this case, com.example.SomeApp, then the session name must be apns-com.example.SomeApp.

Note that the v2 service, sc_push_svc_apns, is deprecated. Use sc_push_svc_apnsv3 instead.

  [
   {mod, 'sc_push_svc_apnsv3'},
   {name, 'apnsv3-com.example.MyApp'},
   {config,
    [{host, "api.push.apple.com"},
     {port, 443},
     {apns_env, prod},
     {bundle_seed_id, <<"com.example.MyApp">>},
     {apns_topic, <<"com.example.MyApp">>},
     {retry_delay, 1000},
     {disable_apns_cert_validation, true},
     {ssl_opts,
      [{certfile, "/some/path/com.example.MyApp.cert.pem"},
       {keyfile, "/some/path/com.example.MyApp.key.unencrypted.pem"},
       {cacertfile, "/some/path/cacerts.crt"},
       {honor_cipher_order, false},
       {versions, ['tlsv1.2']},
       {alpn_preferred_protocols, [<<"h2">>]}
      ]
     }
    ]
   }
  ]
  [
   {mod, 'sc_push_svc_gcm'},
   {name, 'gcm-com.example.SomeApp'},
   {config,
    [
     %% Required GCM API key
     {api_key, <<"ahsgdblahblahfjkjkjfdk">>},
     %% Required, even if empty list. Defaults shown.
     {ssl_opts, [
                 {verify, verify_peer},
                 {reuse_sessions, true}
                ]},
     %% Optional, defaults as shown.
     {uri, "https://gcm-http.googleapis.com/gcm/send"},
     %% Optional, omitted if missing.
     {restricted_package_name, <<"my-android-pkg">>},
     %% Maximum times to try to send and then give up.
     {max_attempts, 10},
     %% Starting point in seconds for exponential backoff.
     %% Optional.
     {retry_interval, 1},
     %% Maximum seconds for a request to live in a retrying state.
     {max_req_ttl, 3600},
     %% Reserved for future use
     {failure_action, fun(_)}
    ]}
  ]

stop_service/1


stop_service(Id) -> Result
  • Id = pid() | child_id()
  • Result = ok | {error, term()}

Stops a service and all sessions for that service.

stop_session/2


stop_session(Service, Name) -> Result
  • Service = atom()
  • Name = atom()
  • Result = ok | {error, Reason}
  • Reason = term()

Stop named session.