The Push Service allows a push-enabled application to send requests to the Push Proxy Gateway (PPG). The PPG provides a set of services that enables interaction between content providers and push-enabled applications.
The Push Service allows a push-enabled application to send requests to the Push Proxy Gateway (PPG). The PPG provides a set of services that enables interaction between content providers and push-enabled applications.
The Push Service supports both sending requests to and receiving responses from the PPG. Applications that receive response messages while they are running are notified via the push_callback_t function.
The application can send an application-level acknowledgment that indicates whether the application accepted or rejected the pushed content (push_service_accept_push() or push_service_reject_push()).
Initialize BlackBerry Platform Services (BPS) (for more information about BPS, see bps/bps.h)
Establish Interaction with the Push Notification Service (PNS) Agent. The PNS Agent is a software component that maintains a connection with the PPG, and forwards push notifications that it receives from the PPG to the appropriate instance of the push-enabled application.
Request navigator events and listen for events
Receive Push messages
Shutdown your application
Shutdown BPS
// ******************************** // See section Establish Interaction with the PNS Agent // ********************************Go to the section below titled "Establish interaction with the PNS Agent" to see the code snippets and explanations.
int main(int argc, char *argv[])
{
// Initialize BPS
if (bps_initialize() == BPS_FAILURE){
printf("Failed to initialize bps\n");
return EXIT_FAILURE;
}
// ********************************
// See section Establish Interaction with the PNS Agent
// ********************************
// To keep things simple just show a blue screen
show_screen(0xff0000ff);
// Signal BPS library that navigator and screen events will be
// requested
screen_request_events(screen_ctx);
navigator_request_events(0);
while (!shutdown) {
// Handle user input
handle_event();
}
// ********************************
// See section Application shutdown
// ********************************
// Shutdown BPS
screen_stop_events(screen_ctx);
bps_shutdown();
screen_destroy_window(screen_win);
screen_destroy_context(screen_ctx);
printf("Main Loop exit.\n");
return EXIT_SUCCESS;
}
void handle_event()
{
bps_event_t *event = NULL;
int rc = bps_get_event(&event, -1);
if (rc != BPS_SUCCESS) {
ERR("BPS get event failed.");
}
else if (event) {
int domain = bps_event_get_domain(event);
printf("Got domain[%d]\n", domain);
if (domain == navigator_get_domain()) {
printf("Received Navigator event\n");
// ********************************
// See section Receiving a Push Message
// ********************************
handle_navigator_event(event);
}
}
}
Create a push_service_t structure to invoke the APIs from the Push Service library. Obtain the Push Service file descriptor using push_service_get_fd(), and add it to the list of file descriptors monitored by BPS using bps_add_fd(). The I/O handler is called by BPS when activity is detected on the Push Service file descriptor.
int main(int argc, char *argv[])
{
// .. omitted for brevity
push_service_t *ps = NULL;
int rc = push_service_initialize(&ps);
if (rc == PUSH_FAILURE || (ps == NULL)) {
printf("start_push_service: failed init push_service
errno[%d] error[%s]\n", errno,
strerror(errno));
return EXIT_FAILURE;
}
int push_pps_fd = push_service_get_fd(ps);
if (push_pps_fd == PUSH_INVALID_PPS_FILE_DESCRIPTOR) {
printf("invalid push pps file descriptor\n");
return EXIT_FAILURE;
}
// Add the Push Service file descriptor to a list of
// descriptors monitored by BPS.
if (bps_add_fd(pushPpsFd, BPS_IO_INPUT, &push_io_handler, ps)
== BPS_FAILURE) {
printf("Failed to add push file descriptor %d to bps\n",
pushPpsFd);
return EXIT_FAILURE;
}
// .. omitted for brevity
}
// Push io handler
int push_io_handler(int fd, int io_events, void* opaque) {
int rc = BPS_FAILURE;
push_service_t* ps = (push_service_t*)opaque;
if (ps != NULL) {
int old_fd = push_service_get_fd();
printf("calling push service to process the incoming
PPS message\n");
if (push_service_process_msg(ps) == PUSH_SUCCESS) {
int new_fd = push_service_get_fd();
// Push connection has been closed.
// Need to remove the Push Service file descriptor from
// the list monitored by BPS.
if (new_fd == PUSH_INVALID_PPS_FILE_DESCRIPTOR) {
if (bps_remove_fd(old_fd) == BPS_FAILURE) {
printf("Failed to remove PPS file descriptor\n");
}
}
rc = BPS_SUCCESS;
}
}
return rc;
}
rc = push_service_set_provider_application_id(ps, provider_app_id); if (rc == PUSH_FAILURE) { printf("push_service_set_provider_application_id: errno[%d]" "error[%s]\n", errno, strerror(errno)); return false; } rc = push_service_set_target_key(ps, target_key); if (rc == PUSH_FAILURE) { printf("push_service_set_target_key: errno[%d] error[%s]\n", errno, strerror(errno)); return false; } rc = push_service_create_session(ps, on_create_session_complete); if (rc == PUSH_FAILURE) { printf("push_service_create_session: errno[%d] error[%s]\n", errno, strerror(errno)); return false; }
Calls to create sessions are asynchronous. The provided on_create_session_complete() callback function will be invoked when the push_service_create_session() request is complete.
void on_create_session_complete(push_service_t* ps, int status_code) { printf("create_session_complete called status_code[%d]", status_code); if (status_code == PUSH_NO_ERR) { rc = push_service_set_ppg_url(ps, ppg_url); if (rc == PUSH_SUCCESS) { rc = push_service_create_channel(ps, on_create_channel_complete, create_channel_on_push_transport_ready); } else { printf("push_service_set_ppg_url: errno[%d] error[%s]", errno, strerror(errno)); } } }
You need to create a channel with the PPG so that your application and the device it runs on are enlisted to receive content whenever the PPG sends content. The request to create a channel is sent through the PNS Agent. You need the PPG URL to invoke the call to create a channel.
The callback functions cannot be NULL. The create_channel_on_push_transport_ready() callback function is used to handle the scenario where the push_service_create_channel() request failed with a PUSH_ERR_TRANSPORT_FAILURE (10103) or PUSH_ERR_PPG_SERVER_ERROR (10110) status code.
void on_create_channel_complete(push_service_t* ps, int status_code) { printf("on_create_channel_complete called statusCode[%d]", status_code); } void create_channel_on_push_transport_ready(push_service_t* ps, int status_code) { push_service_create_channel(ps, on_create_channel_complete, create_channel_on_push_transport_ready); } // ... rc = push_service_set_ppg_url(ps, ppg_url); if (rc == PUSH_SUCCESS) { rc = push_service_create_channel(ps, on_create_channel_complete, create_channel_on_push_transport_ready); } else { printf("push_service_set_ppg_url: errno[%d] error[%s]", errno, strerror(errno)); }
On application shutdown, you should explicitly deallocate any memory allocated for a given push_service_t structure using push_service_cleanup(). You also need to remove the Push Service file descriptor from the list monitored by BPS using bps_remove_fd().
rc = push_service_cleanup(ps); if (rc == PUSH_FAILURE) { printf("push_service_cleanup: errno[%d] error[%s]\n", errno, strerror(errno)); } if (bps_remove_fd(pushPpsFd) == BPS_FAILURE){ printf("Failed to remove PPS file descriptor\n"); }
To receive a push message you need to listen for a navigator invoke event in your main event loop. The action value of this event must be PUSH_INVOCATION_ACTION. If these criteria are met, then you can extract the invoke data into a push_payload_t structure using push_payload_create() and push_payload_set_payload(). Before using the structure, check that the data in the structure is valid using push_payload_is_valid(). After you finish using the push_payload_t structure, you should explicitly deallocate any memory given to push_payload_t using push_payload_destroy().
void handle_navigator_event(bps_event_t *event) {
int event_type = bps_event_get_code(event);
printf("received event type [%d]\n", event_type);
switch (event_type) {
case NAVIGATOR_EXIT:
printf("NAVIGATOR Exit event\n");
shutdown = true;
break;
case NAVIGATOR_INVOKE_TARGET: {
// Our handler was invoked
const navigator_invoke_invocation_t *invoke =
navigator_invoke_event_get_invocation(event);
if(invoke) {
const char *action =
navigator_invoke_invocation_get_action(invoke);
printf("Got invoke: action='%s'\n", action ? action : "NULL");
if (strcmp(action,PUSH_INVOCATION_ACTION) == 0){
// get the jsonData from the invoke object and
// put it into a push_payload_t struct
const unsigned char* raw_invoke_data =
(unsigned char*)
navigator_invoke_invocation_get_data(invoke);
int invoke_data_len =
navigator_invoke_invocation_get_data_length(invoke);
printf("Creating push_payload_t from raw_invoke_data\n");
push_payload_t* push_payload;
if(push_payload_create(&push_payload) == PUSH_FAILURE){
printf("failed to create push_payload. errno[%d]\n",
errno);
return FAILURE;
}
if (push_payload_set_payload(push_payload,
raw_invoke_data,
invoke_data_len) ==
PUSH_SUCCESS && push_payload_is_valid(push_payload)){
printf("push_payload_t is valid\n");
process_push_payload(push_payload);
} else {
printf("push_payload_t is NOT valid\n");
}
// cleanup
push_payload_destroy(push_payload);
}
}
break;
}
default:
break;
}
}
Here is an example of how to retrieve the data from a push_payload_t structure, and how to loop through all of the headers.
void process_push_payload(const push_payload_t* payload) { const unsigned char* data = push_payload_get_data(payload); size_t data_length = push_payload_get_data_length(payload); printf("data length : [%d]\n", data_length); size_t headers_length = push_payload_get_headers_length(payload); for(int i=0; i <headers_length;i++){ const push_header_t* header = push_payload_get_header(payload,i); if (header){ const char* header_name = push_header_get_name(header); const char* header_value = push_header_get_value(header); if (header_name){ printf("Header name: [%s]\n", header_name); } if (header_value){ printf("Header value: [%s]\n", header_value); } } } }
When the SIM card is changed on a device, the PNS Agent automatically destroys the channel. You can implement a callback function to handle the SIM change.
push_service_set_sim_change_callback(on_sim_change);
A call to push_service_create_channel() is recommended.
void on_sim_change(push_service_t* ps) { push_service_create_channel(ps, on_create_channel_complete, create_channel_on_push_transport_ready); }
If the connection to the PNS Agent has been closed, the application or service needs to re-establish the connection with the PNS Agent by calling push_service_get_fd() periodically until push_service_get_fd() returns a valid file descriptor. You can implement a callback function to handle when the connection closes.
push_service_set_connection_close_callback(ps, on_connection_closed);
Calling push_service_get_fd() in a backoff timer is recommended.
void on_connection_closed(push_service_t* ps) { // Start backoff timer which periodically calls // push_service_get_fd() until push_service_get_fd() returns a // valid file descriptor. }