Push Service Library
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.
Overview
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); } } }
Establish interaction with the PNS Agent
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)); } } }
Create a channel with the PPG
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)); }
Application shutdown
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"); }
Receiving a Push Message
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; } }
Processing a Push Payload Structure
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); } } } }
Detecting a changed SIM card
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); }
Detecting when the PNS Agent connection is closed
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. }
Last modified: 2014-05-14