This module is the Erlang API for Silent Circle push notifications and registrations.
Authors: Edwin Fine (efine@silentcircle.com
).
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}]}]}]
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
notification() = [alert_spec() | tag_spec() | service_specific_spec() | receivers()]
props() = proplists:proplist()
receiver_regs() = filtered_result()
receiver_spec() = tag_spec_mult() | svc_tok_spec() | svc_appid_tok_spec() | device_id_spec()
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_ok() = {ok, reg_info_list()}
reg_results() = [reg_result()]
send_result() = sync_send_result() | async_send_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_spec() = {svc_appid_tok, svc_appid_toks()}
svc_appid_toks() = [svc_appid_tok()]
svc_tok_spec() = {svc_tok, svc_toks()}
svc_toks() = [svc_tok()]
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>>
.
async_send/1 | Asynchronously sends a notification specified by proplist
Notification . |
async_send/2 | |
deregister_device_ids/1 | Deregister multiple registered device IDs. |
deregister_id/1 | Deregister a registered ID. |
deregister_ids/1 | Deregister multiple registered IDs. |
get_all_service_configs/0 | Get all service configurations. |
get_registration_info_by_id/1 | Get registration info corresponding to a device ID. |
get_registration_info_by_svc_tok/2 | Get registration info corresponding to service and reg id. |
get_registration_info_by_tag/1 | Get registration info corresponding to a tag. |
get_service_config/1 | Get service configuration. |
get_session_pid/1 | Get pid of named session. |
get_version/0 | Get scpf version string as a binary. |
make_service_child_spec/1 | Make a supervisor child spec for a service. |
quiesce_all_services/0 | Quiesce all services. |
quiesce_service/1 | Quiesce a service. |
quiesce_session/2 | Quiesce session. |
register_id/1 | Register to receive push notifications. |
register_ids/1 | Perform multiple registrations. |
register_service/1 | Register a service in the service configuration registry. |
send/1 | Send a notification specified by proplist Notification . |
send/2 | |
start/0 | |
start_service/1 | Start a push service. |
start_session/2 | Start named session as described in the options proplist Opts . |
stop_service/1 | Stops a service and all sessions for that service. |
stop_session/2 | Stop named session. |
async_send(Notification) -> Result
Notification = notification()
Result = async_send_results()
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(Notification, Opts) -> Result
Notification = notification()
Opts = std_proplist()
Result = async_send_results()
deregister_device_ids(IDs::[binary()]) -> ok | {error, term()}
Deregister multiple registered device IDs.
deregister_id(ID::sc_push_reg_db:reg_id_key()) -> ok | {error, term()}
Deregister a registered ID.
deregister_ids(IDs::reg_id_keys()) -> ok | {error, term()}
Deregister multiple registered IDs.
get_all_service_configs() -> SvcConfigs
SvcConfigs = [std_proplist()]
Get all service configurations
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(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(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(Service) -> Result
Service = term()
Result = {ok, std_proplist()} | {error, term()}
Get service configuration
See also: start_service/1.
get_session_pid(Name) -> Result
Name = atom()
Result = pid() | undefined
Get pid of named session.
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(Opts) -> Result
Opts = std_proplist()
Result = supervisor:child_spec()
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() -> ok
Quiesce all services.
See also: 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(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(Props::sc_types:reg_proplist()) -> sc_types:reg_result()
Register to receive push notifications.
Props
is a proplist with the following elements, all of which are
required:
{service, string()}
apns
for
Apple Push, and gcm
for Android (Google Cloud Messaging) push.
{token, string()}
{tag, string()}
{device_id, string()}
{app_id, string()}
com.example.MyApp
.
The exact format of application ID varies between services.
{dist, string()}
"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(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(Svc) -> ok
Svc = std_proplist()
Register a service in the service configuration registry.
See also: start_service/1.
send(Notification) -> Result
Notification = notification()
Result = sync_send_results()
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(Notification, Opts) -> Result
Notification = notification()
Opts = std_proplist()
Result = sync_send_results()
start() -> any()
start_service(ServiceOpts) -> Result
ServiceOpts = std_proplist()
Result = {ok, pid()} | {error, term()}
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()}
apns
for
Apple Push, and gcm
for Android (Google Cloud Messaging) push.
There is also a built-in null
service for testing.
{mod, atom()}
{description, string()}
{sessions, list()}
start_session/1
for details.
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(Id) -> Result
Id = pid() | child_id()
Result = ok | {error, term()}
Stops a service and all sessions for that service.
stop_session(Service, Name) -> Result
Service = atom()
Name = atom()
Result = ok | {error, Reason}
Reason = term()
Stop named session.