Code samples

The code samples are from our headless and UI sample applications. They show you how to use the Push Service APIs of the BlackBerry 10 Native SDK to do the following:

  • Create a PushService object so that your application can create a push session and perform push-related operations. The push session is required to establish communication with the 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 application instance.
  • Start running your headless application in the background if it isn't already running when a push message arrives.
  • Create a push channel to the PPG and subscribe with the Push Initiator. The Push Initiator uses the push channel to send push notifications to our sample application.
  • Receive a push message.
  • Acknowledge the receipt of a push message.
  • Handle notifications in the BlackBerry Hub.
  • Destroy a push channel and unsubscribe from the Push Initiator.
  • Automatically detect a changed SIM card and stop receiving push messages on the device.
  • Handle a push transport error and a PPG server error.
  • Handle a lost connection with the PNS agent.

Creating a PushService object

Our sample application creates a PushService object in order to create a push session and perform push-related operations. In your application, you should create one global instance of the PushService object, and associate it with one providerApplicationID and one invokeTargetID.

providerApplicationID and invokeTargetID are parameters of the PushService() constructor. The providerApplicationID parameter corresponds to the application ID that you specified when you configured our sample application. For details about configuring our sample application, see Configure our sample application.

The invokeTargetID parameter corresponds to the value that's specified in the bar-descriptor.xml file for our sample application. The invokeTargetID identifies the push-enabled application that's the target of a push message, and that's invoked when the push message arrives. For more information about updating your bar-descriptor.xml file, see Update your bar-descriptor.xml file.

Our sample application must create a push session to establish communication with the PNS agent running on the device, and before it can use any of the other Push Service APIs. Our application must create a session every time it starts. Since the call to create a session is asynchronous, our sample application connects a slot to the createSessionCompleted signal before the call to create a session.

Call initializePushService()

The initializePushService() function initializes one instance of bb::network::PushService. The function also connects the signals for the following events: create session, create channel, destroy channel, register to launch, unregister from launch, SIM card change, push transport ready, and a lost connection with the PNS agent.

The initializePushService() function is defined in the PushNotificationService object. This object is in the service layer of our application. To pass the PushService signals up to the main object of our headless application, we chain the PushService signals together by connecting them to the PushNotificationService signals.

void PushNotificationService::initializePushService()
{
  const Configuration config = m_configurationService.configuration();

  // ...
     m_previousApplicationId = config.providerApplicationId();

     m_pushService = new PushService(config.providerApplicationId(), 
        INVOKE_TARGET_KEY_PUSH, this);

     //Connect the signals
     checkConnectResult(QObject::connect(m_pushService, 
        SIGNAL(createSessionCompleted(const 
        bb::network::PushStatus&)), this, 
        SIGNAL(createSessionCompleted(const 
        bb::network::PushStatus&))));
     checkConnectResult(QObject::connect(m_pushService, 
        SIGNAL(createChannelCompleted(const bb::network::PushStatus&, 
        const QString)), this, SIGNAL(createChannelCompleted(const 
        bb::network::PushStatus&, const QString))));
     checkConnectResult(QObject::connect(m_pushService, 
        SIGNAL(destroyChannelCompleted(const 
        bb::network::PushStatus&)), this, 
        SIGNAL(destroyChannelCompleted(const 
        bb::network::PushStatus&))));
     checkConnectResult(QObject::connect(m_pushService, 
        SIGNAL(registerToLaunchCompleted(const 
        bb::network::PushStatus&)), this, 
        SIGNAL(registerToLaunchCompleted(const 
        bb::network::PushStatus&))));
     checkConnectResult(QObject::connect(m_pushService, 
        SIGNAL(unregisterFromLaunchCompleted(const 
        bb::network::PushStatus&)), this, 
        SIGNAL(unregisterFromLaunchCompleted(const 
        bb::network::PushStatus&))));
     checkConnectResult(QObject::connect(m_pushService, 
        SIGNAL(simChanged()), this, SIGNAL(simChanged())));
     checkConnectResult(QObject::connect(m_pushService, 
        SIGNAL(pushTransportReady(bb::network::PushCommand::Type)),
        this, 
        SIGNAL(pushTransportReady(bb::network::PushCommand::Type))));
     checkConnectResult(QObject::connect(m_pushService, 
        SIGNAL(connectionClosed()), this, 
        SLOT(onConnectionClosed())));
  }
}

Check the connect signal result

The initializePushService() function uses the checkConnectResult() function to check for signal and slot connection failures. Our sample application checks all signal and slot connections using the checkConnectResult() function.

void checkConnectResult(bool connectResult){
    // If any Q_ASSERT statement(s) indicate that the slot failed to 
    // connect to the signal, make sure you know exactly why this has 
    // happened. This is not normal, and will cause your app to stop 
    // working!!

    // This is only available in Debug builds.
    Q_ASSERT(connectResult);
}

Connect a slot to the createSessionCompleted() signal

Our sample application connects a slot to the createSessionCompleted() signal.

AppHeadless::AppHeadless()
{
// ...

// Connect the push notification service signals and slots.
checkConnectResult(QObject::connect(m_pushNotificationService, 
   SIGNAL(createSessionCompleted(const bb::network::PushStatus&)),
   this, 
   SLOT(onCreateSessionCompleted(const bb::network::PushStatus&))));
// ...
}

Call createSession()

Our sample application calls createSession() from the PushNotificationService object to create a push session.

void PushNotificationService::createSession()
{
  // Initialize the PushService if it has not been already.
  initializePushService();
 
  m_pushService->createSession();
}

Starting our sample application in the background

After our sample application successfully creates a push session, it can call registerToLaunch() so that our headless application can start running in the background if it isn't already running when a push message arrives. The registerToLaunch() function is called in the onCreateSessionCompleted slot. In your application, if you try to call registerToLaunch() before you successfully create a push session, you'll get an error on the register to launch operation.

The register to launch flag is saved between application restarts only if we create a push channel or if the channel remains active. If we don't create a channel or the create channel operation fails, the register to launch flag isn't saved. When a channel is destroyed, the flag is lost.

If you don't want to start your application when a push message arrives, call unregisterFromLaunch(). The push messages that arrive after you call unregisterFromLaunch() are delivered only if your application is running when the messages arrive. Additionally, the default starting behavior for your application is unregisterFromLaunch(), so you would call unregisterFromLaunch() only if you called registerToLaunch() previously. For more information about registerToLaunch() and unregisterFromLaunch(), see the API Reference.

void AppHeadless::onCreateSessionCompleted(const 
   bb::network::PushStatus &status)
{

	QString message;
    if (status.code() == PushErrorCode::NoError) {
        if (m_shouldRegisterToLaunch) {
            m_pushNotificationService->registerToLaunch();
        } else if (m_shouldUnregisterFromLaunch) {
            m_pushNotificationService->unregisterFromLaunch();
        }
    }

    sendStatusToUI(PUSH_COLLECTOR_CREATE_SESSION_COMPLETE, 
       message, status.code());
}

Creating a push channel

Our sample application creates a push channel to the PPG to receive push messages. Our application only needs to create a channel the first time it starts or if a destroy channel occurred previously and we want to start receiving push messages again.

If you chose to subscribe with the Push Service SDK when you configured our sample application, the Push Initiator requires a username and password that it authenticates before allowing the subscription. Our application provides a Register dialog box where the user can type any username and password. For your application, check with your push content provider to get a valid username and password.

You can open the Register dialog box by tapping Register at the bottom of the device screen. The Register dialog box appears.

Register dialog box where you type a username and password.

After you type the username and password, and tap Register in the dialog box, our sample application tries to create a channel.

If you're not subscribing with the Push Service SDK, you don't need to provide a username and password. You can simply tap Register at the bottom of the device screen, and our application tries to create a channel.

Connect a slot to the createChannelCompleted() signal

Since the call to create a channel is asynchronous, our sample application connects a slot to the createChannelCompleted() signal.

AppHeadless::AppHeadless()
{
// ...
 checkConnectResult(QObject::connect(m_pushNotificationService, 
   SIGNAL(createChannelCompleted(const bb::network::PushStatus&, 
   const QString)), this, SLOT(onCreateChannelCompleted(const 
   bb::network::PushStatus&, const QString))));
// ...
}

Call createChannel()

Our sample application calls createChannel() from the PushNotificationService object to create a channel, and passes in the PPG URL in the parameter list that you specified when you configured our application. If you're using the BlackBerry Device Service as the PPG, the PPG URL is an empty string. If the channel creation is successful, the createChannelCompleted signal returns a token that represents the address of the device. The Push Initiator uses the token to send push messages to the device. If the channel creation is unsuccessful, our sample application displays an error message.

void PushNotificationService::createChannel()   
{
 m_pushService->createChannel(m_configurationService.configuration().
   ppgUrl());   
}

Call subscribeToPushInitiator()

If the channel creation is successful, our sample application calls subscribeToPushInitiator() from the PushNotificationService object to subscribe with the Push Initiator, and passes off the call to the RegisterService object. The subscribeToPushInitiator() parameter list includes the token to the Push Initiator and the username and password that you typed in the Register dialog box. If you're not subscribing with the Push Service SDK, our sample application doesn't call subscribeToPushInitiator().

void PushNotificationService::subscribeToPushInitiator(
    const User& user, const QString &token)
{
  m_registerService.subscribeToPushInitiator(user,token);
}

Handle the subscription with the Push Initiator

The RegisterService object handles the subscription with the Push Initiator.

void RegisterService::subscribeToPushInitiator(const User& user, const 
   QString& token)
{
   // Keep track of the current user's information so it can be stored 
   // later on a success
   m_currentUser = user;

   const Configuration config = m_configurationService.configuration();

   QUrl url(config.pushInitiatorUrl() + "/subscribe");
   url.addQueryItem("appid",config.providerApplicationId());
   url.addQueryItem("address",token);
   url.addQueryItem("osversion",deviceVersion());
   url.addQueryItem("model",deviceModel());
   url.addQueryItem("username",user.userId());
   url.addQueryItem("password",user.password());
   if (config.usingPublicPushProxyGateway()) {
       url.addQueryItem("type","public");
   } else {
       url.addQueryItem("type","bds");
   }

   m_reply = m_accessManager.get(QNetworkRequest(url));

   // Connect to the reply finished signal.
    
   checkConnectResult(connect(m_reply, SIGNAL(finished()), this, 
     SLOT(httpFinished())));
}

Receiving a push message

Our sample application is divided into two parts: a UI application and a headless application. Both use their own instance of InvokeManager, and they each listen for different types of invoke events. It's essential that the UI application and headless application connect to the InvokeManager signal at the beginning of the application. If they don't, the applications might not receive the signal.

The invoke request can be a push invoke request or an open invoke request. A push invoke request is processed by the headless application, and occurs when the application receives a push message, a start event, or a command from the UI application. An open invoke request is processed by the UI application, and occurs when a user taps a notification for a push message in the BlackBerry Hub. The following code sample shows you how our headless application handles a push invoke request. To see how our UI application handles an open invoke request, see Handling notifications in the BlackBerry Hub.

checkConnectResult(connect(m_invokeManager, SIGNAL(invoked(const 
   bb::system::InvokeRequest&)), SLOT(onInvoked(const 
   bb::system::InvokeRequest&))));

The onInvoked() function first checks the action property of InvokeRequest. If the property matches the string constant BB_PUSH_INVOCATION_ACTION, the function then checks to see that the bb::network::PushService instance is created, and that the application ID is stored. If both are true, the headless application extracts the push message from the invoke request by passing the invoke request into the PushPayload constructor. The pushNotificationHandler() function then processes the push message. In your application, you can provide the code to process the push message in a way that's appropriate for your environment.

If the action property matches the string constant BB_PUSH_COLLECTOR_COMMAND_ACTION, this means that the UI application sent a command to the headless application. The headless application extracts the command from the invoke request by decoding the JSON data into a QVariantList. The commandMessageHandler() function then processes the command. The command object that is passed from the UI application to the headless application is specific to our application, and shows you one way to communicate between UI and headless applications. In your application, you can use a communication mechanism that's appropriate for your application.

void AppHeadless::onInvoked(const InvokeRequest& request)
{
  if (request.action().compare(BB_PUSH_INVOCATION_ACTION) == 0) {
    if (m_configurationService.hasConfiguration()) {
  	 // The underlying PushService instance might not have been
  	 // initialized when an invoke first comes in.
  	 // Make sure that we initialize it here if it hasn't been
       // already. It requires an application ID (for consumer 
  	 // applications) so we have to check that configuration 
       // settings have already been stored.
  	 m_pushNotificationService->initializePushService();

	  	qDebug() << "Received push action";

	  	// Received an incoming push.
	  	// Extract it from the invoke request and then process 
          // it.
		  PushPayload payload(request);
		  if (payload.isValid()) {
		  	pushNotificationHandler(payload);
		  }
		}
	} else if (request.action().
          compare(BB_PUSH_COLLECTOR_COMMAND_ACTION) == 0){
		qDebug() << "Received command action" ;
		QVariantList commandData = m_jsonDA->
          loadFromBuffer(request.data()).toList();
		commandMessageHandler(commandData);
	}
}

Acknowledging the receipt of a push message

The Push Initiator can send a confirmed push message with application-level reliability that requires an acknowledgement from the push-enabled application. The push-enabled application can determine if it received a confirmed push message by checking the Boolean property isAckRequired from the PushPayload class. If the property is set to true, the push-enabled application must acknowledge the push message by either accepting or rejecting it using the acceptPush() or rejectPush() functions from the PushService class. For more information about these functions, see the API Reference.

Our sample application checks the isAckRequired property when it calls the pushNotificationHandler() function. Our sample application acknowledges the receipt of the push message by accepting it using the acceptPush() function.

// If an acknowledgement of the push is required (that is, the push  
// was sent as a confirmed push, which is equivalent terminology to  
// the push being sent with application level reliability), then you  
// must either accept or reject the push.
if (pushPayload.isAckRequired()) {
   // In our sample, we always accept the push, but situations might  
   // arise where an application might want to reject the push. For  
   // example, after looking at the headers that came with the push  
   // or the data of the push, we might decide that the push received 
   // did not match what we expected, so we might want to reject it.
   m_pushNotificationService->acceptPush(pushPayload.id());

Handling notifications in the BlackBerry Hub

Our sample application handles notifications of push messages in the BlackBerry Hub in the following ways:

  • When a user taps on a notification in the BlackBerry Hub, our UI application receives an open invoke event, and displays the push message that corresponds to the notification.
  • When our headless application receives a push message, it adds a notification to the BlackBerry Hub.
  • When a user taps on the push message in our UI application, our UI application removes the notification from the BlackBerry Hub.
  • When a user deletes an individual push message in our UI application, our UI application removes the notification for the push message from the BlackBerry Hub.
  • When a user taps Mark All Open or Delete All in our UI application, our UI application removes all the notifications for our application from the BlackBerry Hub.

Display a push message for an open invoke event

Our UI application connects a slot to the InvokeManager invoked signal to receive invoke requests. An open invoke request occurs when a user taps a notification for a push message in the BlackBerry Hub. It's essential that we connect to the InvokeManager signal at the beginning of the application. If we don't, our application might not receive the signal.

// Connect the InvokeManager signals and slots.

checkConnectResult(connect(m_invokeManager, SIGNAL(invoked(const 
  bb::system::InvokeRequest&)), SLOT(onInvoked(const 
  bb::system::InvokeRequest&))));

The onInvoked() function checks the action property of InvokeRequest to see if it matches the string constant BB_OPEN_INVOCATION_ACTION. If the property matches the constant, the user tapped a notification in the BlackBerry Hub. Our sample application displays the push message that corresponds to the notification.

void App::onInvoked(const InvokeRequest &request)
{
	if (request.action().compare(BB_OPEN_INVOCATION_ACTION) == 0){
		qDebug() << "Received open action";
		// Received an invoke request to open an existing push 
        // (ie. from a notification in the BlackBerry Hub).
		// The payload from the open invoke is the seqnum for 
        // the push in the database
		openPush(request.data().toInt());
	}
}

Add a notification to the BlackBerry Hub

To add a notification for our sample application to the BlackBerry Hub, our headless application creates a Notification object, adds an InvokeRequest, and calls notify().

// Create a notification for the push that will be added to the 
// BlackBerry Hub.
    Notification *notification = new Notification(NOTIFICATION_PREFIX 
      + QString::number(push.seqNum()),this);
    notification->setTitle("Push Collector");
    notification->setBody(QString("New %0 push received").arg(
      push.fileExtension()));

    // Add an invoke request to the notification
    // This invoke will contain the seqnum of the push.
    // When the notification in the BlackBerry Hub is selected, this  
    // seqnum will be used to lookup the push in the database
    // and display it.
    InvokeRequest invokeRequest;
    invokeRequest.setTarget(INVOKE_TARGET_KEY_OPEN);
    invokeRequest.setAction(BB_OPEN_INVOCATION_ACTION);
    invokeRequest.setMimeType("text/plain");
    invokeRequest.setData(QByteArray::number(push.seqNum()));
    notification->setInvokeRequest(invokeRequest);

    // Add the notification for the push to the BlackBerry Hub
    // Calling this method will add a "splat" to the application icon, 
    // indicating that a new push has been received
    notification->notify();

Notice that the InvokeRequest action matches the bb.action.OPEN action and InvokeRequest MimeType matches the mime-type in the bar-descriptor.xml file. Additionally, the INVOKE_TARGET_KEY_OPEN constant has a value of com.example.pushCollector.invoke.open which matches the invoke-target id in the bar-descriptor.xml file. For more information about the bar-descriptor.xml file, see Update your bar-descriptor.xml file.

Remove a notification from the BlackBerry Hub

To remove a notification for our sample application from the BlackBerry Hub, our application calls deleteFromInbox(), and passes in the sequence number of the push message that needs to be removed. This code is called when the user taps the trash can icon next to a push message in our application, and answers Yes to the confirmation message. Our application also deletes the push message from our application.

Notification::deleteFromInbox(NOTIFICATION_PREFIX + QString::number(
    push.seqNum()));

Remove all notifications from the BlackBerry Hub

To remove all the notifications for our sample application from the BlackBerry Hub, our application calls deleteAllFromInbox(). This code is called when the user taps either Mark All Open or Delete All in our application.

Notification::deleteAllFromInbox();

Destroying a push channel

Our sample application destroys the push channel to the PPG to stop receiving push messages.

If you chose to subscribe with the Push Service SDK when you configured our application, the Push Initiator requires a username and password that it authenticates before unsubscribing. Our sample application provides an Unregister dialog box where the user can type any username and password. For your application, check with your push content provider to get a valid username and password.

You can open the Unregister dialog box by tapping Unregister at the bottom of the device screen. The Unregister dialog box appears.

Unregister dialog box where you type a username and password.

After you type the username and password, and tap Unregister in the dialog box, our sample application tries to destroy the channel.

If you're not subscribing with the Push Service SDK, you don't need to provide a username and password. You can tap Unregister at the bottom of the device screen, and our sample application tries to destroy the channel.

Connect a slot to the destroyChannelCompleted () signal

Since the call to destroy a channel is asynchronous, our sample application connects a slot to the destroyChannelCompleted() signal.

AppHeadless::AppHeadless()
{
// ...
checkConnectResult(QObject::connect(m_pushNotificationService, 
  SIGNAL(destroyChannelCompleted(const bb::network::PushStatus&)),
  this, SLOT(onDestroyChannelCompleted(const 
  bb::network::PushStatus&))));
// ...
}

Call destroyChannel()

Our sample application calls destroyChannel() from the PushNotificationService object to destroy a channel.

void PushNotificationService::destroyChannel()   
{
  m_pushService->destroyChannel();   
}

Call unsubscribeFromPushInitiator()

If the destroy channel is successful, our sample application calls unsubscribeFromPushInitiator() from the PushNotificationService object to unsubscribe from the Push Initiator, and passes off the call to the UnregisterService object. The parameter list includes the username and password that you typed in the Unregister dialog box. If you're not subscribing with the Push Service SDK, our sample application doesn't call unsubscribeFromPushInitiator().

void PushNotificationService::unsubscribeFromPushInitiator(const 
     User &user)
{
  m_unregisterService.unsubscribeFromPushInitiator(user);
}

Handle the unsubscribe operation from the Push Initiator

The UnregisterService object handles the unsubscribe operation from the Push Initiator.

void UnregisterService::unsubscribeFromPushInitiator(const 
   User& user)
{
   // Keep track of the current user's information.
   // If it matches the one stored then it will be removed on success. 
   m_currentUser = user;
 
   const Configuration config = m_configurationService.configuration();
 
   QUrl url(config.pushInitiatorUrl() + "/unsubscribe");
   url.addQueryItem("appid",config.providerApplicationId());
   url.addQueryItem("username",user.userId());
   url.addQueryItem("password",user.password());
     
   m_reply = m_accessManager.get(QNetworkRequest(url));
 
   // Connect to the reply finished signal.
    
   checkConnectResult(connect(m_reply, SIGNAL(finished()), this, 
     SLOT(httpFinished())));
}

Detecting a changed SIM card

When a user changes a SIM card in a BlackBerry device, the BlackBerry 10 Native SDK automatically destroys the channel. Our sample application removes the user from the database if necessary, and unsubscribes the user from the Push Initiator if you chose to subscribe with Push Service SDK when you configured our sample application. Our sample application also removes all existing push messages and notifications in the BlackBerry Hub that are associated with the user. Our sample application performs these actions for security purposes because a new user might be using the device, and that user shouldn't receive push messages that were intended for the previous user. In your application, you should authenticate all new users.

Our sample application also displays a dialog box that asks if the user wants to register again. Our application performs one of the following actions depending on the answer that the user provides:

  • If the user selects No in the dialog box, our sample application closes the dialog box.
  • If the user selects Yes and you're not subscribing with the Push Service SDK, our sample application tries to create a channel.
  • If the user selects Yes and you're subscribing with the Push Service SDK, our sample application displays the Register dialog box where the user must type a user name and password.

The onSimChanged() function is implemented in the AppHeadless class in our headless application code. Our headless application connects a slot to the simChanged signal.

AppHeadless::AppHeadless()
{
// ...
checkConnectResult(QObject::connect(m_pushNotificationService, 
  SIGNAL(simChanged()), this, SLOT(onSimChanged())));
// ...
}

void AppHeadless::onSimChanged()
{
    // Remove the currently registered user (if there is one)
    // and unsubscribe the user from the Push Initiator since
    // switching SIMs might indicate we are dealing with
    // a different user. Also, remove all pushes and push
	// history from the database.
    m_pushNotificationService->handleSimChange(m_pushHandler);

    sendStatusToUI(PUSH_COLLECTOR_SIM_CHANGE);
}

Handling a push transport error or PPG server error

There might be occasions when our sample application can't create or destroy a channel because of a push transport error or a PPG server error. A push transport error occurs when there's a problem with the mobile network, the user's wireless connection, or the low-level communications layer that the Push Service uses. A PPG server error occurs when the PPG server is unavailable and returns an error. These errors don't occur if you're using the Push Service with the BlackBerry Enterprise Service.

When the channel operations fail because of one of these errors, the Push Service issues an error 10103 (Transport Error) or error 10110 (PPG server error). Our application displays an error message to the user when it receives one of these error codes.

void AppHeadless::onCreateChannelCompleted(const 
  bb::network::PushStatus &status, const QString &token)
{
// ...

    // Typically in your own application you wouldn't want to 
    // display this error to your users.
    QString message = tr("Create channel failed with error code: 
      %0").arg(status.code());

    Configuration config = m_configurationService.configuration();

    switch(status.code()){
    case  PushErrorCode::NoError:

	// ...
      break;
    case  PushErrorCode::TransportFailure:
      message = tr("Create channel failed as the push transport is 
                unavailable. "
                "Verify your mobile network and/or Wi-Fi are turned 
                on. "
                "If they are on, you will be notified when the push 
                transport is available again.");
      break;
    case PushErrorCode::SubscriptionContentNotAvailable:
      message = tr("Create channel failed as the PPG is currently 
                returning a server error. "
                "You will be notified when the PPG is available 
                again.");
      break;
    }

    sendStatusToUI(PUSH_COLLECTOR_CREATE_CHANNEL_COMPLETE, message, 
      status.code());
}

To handle these errors, our sample application connects a slot to the pushTransportReady signal so that the application can try to create or destroy the channel again. The pushTransportReady signal returns a bb::network::PushCommand object which contains the last Push Service API call that failed (either CreateChannel or DestroyChannel).

The onPushTransportReady function checks the value of bb::network::PushCommand to determine which operation failed. The function then calls either createChannel() or destroyChannel() to try to create or destroy the channel.

AppHeadless::AppHeadless()
{
// ...
 checkConnectResult(QObject::connect(m_pushNotificationService, 
   SIGNAL(pushTransportReady(bb::network::PushCommand::Type)),
   this, SLOT(onPushTransportReady(bb::network::PushCommand::Type))));
// ...
}

void AppHeadless::onPushTransportReady(bb::network::PushCommand::Type 
   command)
{
    if (command == PushCommand::CreateChannel){
        m_pushNotificationService->createChannel();
    } else {
        m_pushNotificationService->destroyChannel();
    }
}

Handling a lost connection with the PNS agent

There might be rare occasions when our sample application loses its connection to the PNS agent running on the device. The PNS agent maintains a connection between the PPG and the application, and forwards push notifications from the PPG to the appropriate application instance.

When the application loses the connection, it can't perform these operations: create a session, create or destroy a channel, register to start in the background when a push message arrives, or unregister from starting in the background. When these operations fail because of the lost connection, the Push Service issues a ConnectionClosed error code.

To handle this error, our sample application connects a slot to the connectionClosed signal.

checkConnectResult(QObject::connect(m_pushService, 
   SIGNAL(connectionClosed()), this, SLOT(onConnectionClosed())));

When the connection is lost, our application notifies the user, and then tries to re-establish the connection to the PNS agent by using the m_timer backoff timer to call reconnect(). The timer calls reconnect() periodically, and the interval of time between each call to reconnect() gets longer with each call.

void PushNotificationService::onConnectionClosed()
{
	if (!m_timer->isActive()){
		emit noPushServiceConnection();
		m_timer->start(m_retryTime);
		return;
	}

	// Try to reconnect to the Push Agent
	if (!m_pushService->reconnect()){

		// Reconnect was unsuccessful.
		// Check to see if the max wait time has been reached.
		// If it has, then just stop and notify the user that  
        // reconnecting to the PNS Agent was unsuccessful. Otherwise,  
        // double the amount of time to wait before retrying, and then 
        // try again.
		if (m_retryTime >= MAX_WAIT_TIME){
			qWarning() << "Max number of retry attempts reached. Could 
              not re-establish connection to Push Agent.";
			emit pushAgentConnectionStatusChanged(false);
			m_timer->stop();
		} else {
			qDebug() << "No connection to Push Agent";
			m_retryTime = m_retryTime * 2;
			m_timer->setInterval(m_retryTime);
		}
	} else {
		// Reconnect was successful.
		// Notify the user and call createSession.
		qDebug() << "Connection to Push Agent re-established!";
		m_timer->stop();
		m_retryTime = INITIAL_WAIT_TIME_MILLISECONDS;
		emit pushAgentConnectionStatusChanged(true);
		createSession();
	}

}

Our sample application handles the lost connection by explicitly telling the user that the application is trying to recover from the error. In your application, we recommend that you try to recover from the error silently, and only notify the user if the connection can't be re-established.

Last modified: 2014-11-17



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus