Introduction

About Auto-Pinger

From AllJoyn™ 14.06 onwards,the Ping API allows applications to determine whether a remote peer is still up or not. This puts a burden on the applications however, as they have to take the initiative to do the pinging. Moreover, applications are often interested in an up-to-date state of all remote peers rather than a snapshot view.

To solve this problem, the Auto-Pinger (introduced in R14.12) periodically pings a supplied list of peers on the application's behalf and notifies the application when they become available or leave.

The Auto-Pinger API is currently only available in the C++ language binding of AllJoyn Core.

Goal

The Auto-Pinger supplies the application up-to-date information on which peers are present and which aren't.

Usage

Usage Details

In many cases, applications will want to handle the presence management of similar peers (say, all peers with which the application has an ongoing session) in a similar way. The AutoPinger class tries to facilitate this with the concept of a Ping Group.

When an AutoPinger instance is created, the application first has to define the applicable Ping Groups. Later on, when peers are discovered, the application can add them to one or more Ping Groups (either by unique bus name or by well-known name). The AutoPinger will then periodically perform a Ping() call to all peers in all Ping Groups.

A Ping Group has the following characteristics:

The Ping Group concept offers some advantages:

The Auto-Pinger's functionality can be temporarily suspended (e.g., when an application on a mobile phone goes to the background) by calling the Pause() method. Upon Resume(), the Auto-Pinger continues.

Example Usage

The code snippet below implements a primitive presence manager. It leverages the About discovery mechanism to find all peers on the network that have at least one bus object implementing the org.alljoyn.example.Foo interface. It then adds all these peers to a single Ping Group. When any of these peers stop responding to ping requests, or resume their ping responses, the appropriate callback is invoked.

class PresenceManager : private ajn::AboutListener,
                        private ajn::PingListener {

  private:
    BusAttachment& bus;
    AutoPinger autoPinger;
    std::set<qcc::String> peers;
    static const int PING_INTERVAL = 30; /* seconds */
    static const char* PING_GROUP = "allpeers";

  public:
    /* defined in ajn::PingListener */
    virtual void DestinationLost(const qcc::String& group, const qcc::String& destination) {
        std::cout << "Peer " << destination << " is not responding."<< std::endl;
    }

    /* defined in ajn::PingListener */
    virtual void DestinationFound(const qcc::String& group, const qcc::String& destination) {
        std::cout << "Peer " << destination << " responds to ping requests." << std::endl;
    }

    /* defined in ajn::AboutListener */
    virtual void Announced(const char* busName,
                           uint16_t version,
                           ajn::SessionPort port,
                           const MsgArg& objectDescription,
                           const MsgArg& aboutData) {

        if (peers.count(busName) == 0) {
            /* Add destination to ping group */
            autoPinger.AddDestination(PING_GROUP, busName);
            peers.insert(busName);
        }
    }

    PresenceManager(BusAttachment& _bus) : bus(_bus), autoPinger(bus) {
        /* Create a ping group */
        autoPinger.AddPingGroup(PING_GROUP, *this, PING_INTERVAL);

        /* Configure interface-based discovery */
        bus.RegisterAboutListener(*this);
        bus.WhoImplements("org.alljoyn.example.Foo");
    }

    ~PresenceManager() {
        /* Stop interface-based discovery */
        bus.CancelWhoImplements("org.alljoyn.example.Foo");
        bus.UnregisterAboutListener(*this);

        /* Stop AutoPinger */
        autoPinger.RemovePingGroup(PING_GROUP);
    }
};

Note that an actual production-grade implementation of a presence manager would be more complex than this basic example:

Best practices

Choosing the Right Ping Interval

Choosing the right ping interval is always a trade-off between having the most up-to-date information and not wasting network and energy resources. When the application is user facing, there are typically higher requirements to the up-to-dateness of the remote peer state compared to when the application is in the background. Also, when a remote peer has not been seen for a significant amount of time, it's safe to assume the remote peer has gone and you can ping it at a lower interval or even stop pinging it altogether. When the remote peer starts again, it will send a new announcement.

Modular Applications

Share the AutoPinger Instance

When your application consists of multiple (independently developed) modules, you may want to design your modules in such a way that they can share a single AutoPinger instance. Every AutoPinger instance creates a timer thread to manage the periodic invocation of the Ping() method, so less AutoPingers means less threads in your application. In addition, when multiple modules want to keep track of the same peer, the single AutoPinger can optimize the number of Ping calls it performs towards that peer, thus reducing overall network traffic.

Avoid Naming Conflicts

If multiple modules in your application share a single AutoPinger instance, make sure they don't interfere with each other's Ping Groups. The simplest approach here is to let every module have its own prefix for its Ping Group names. Hence, the Foo module's active group would be called FOO.active, and be different from the Bar module's active group, which would be called BAR.active.

Threading Considerations

The PingListener callbacks will be invoked on AllJoyn's dispatcher threads. These are the same threads that are used for the invocation of all other application callbacks (asynchronous method replies, SessionListener callback invocations, etc.). If you want to perform any blocking operation (for example, a synchronous method call) from within a PingListener callback, you must first call BusAttachment::EnableConcurrentCallbacks().