Tracker

From MobiComp

Jump to: navigation, search

Summary

A tracker is a MobiComp component that acts as a context producer. Trackers register their availability and capabilities by sending appropriate information to the ContextService. Their purpose is to collect raw context data from sensors such as GPS receivers and other dynamic sources, or static sources such as configuration files for device capabilities and user-preferences. Trackers transform their input into ContextElements which are then put into the tuplespace managed by the ContextService.

See also:

Developer info

All trackers extend the abstract base class Tracker in the package org.mobicomp.context.tracker in the common branch of the src tree.

From experience with MobiComp v1, we know that many trackers require a thread and run loop. Others, such as the GPSTracker and its subclasses are driven by input from a serial data stream, and so do not require their own thread. Instead, they provide a new ContextElement whenever sufficient data is available.

Initial thoughts on accommodating these two models were to have a basic Tracker class without a thread and a subclass with one. Trackers would then subclass one of these according to whether the thread was required. However, further thought led to the conclusion that the decision whether or not to run the thread should be taken at runtime, and might be enabled/disabled as needed during the lifetime of the subclass. For this reason, we have a single base class in which the thread can be started/stopped as needed.

The current version supports both running and non-running Trackers. The first constructor has no arguments and creates a non-running Tracker:

public Tracker ()

The second has a single long argument:

public Tracker ( long t )

Calling this with a value greater than zero causes the thread to start when the open method is called. The thread loop will execute at intervals of t milliseconds. A negative value has the same effect as calling the no-argument constructor.

The thread interval can be changed using the method:

public void setInterval ( long t )

Again, the parameter is the interval in milliseconds. If the thread is not running, because open() has not yet been called, a negative value prevents the thread starting when open() is called. If the thread is already running, a zero or positive value will result in an immediate change, but a negative value will have no effect until the thread has been stopped by calling close() and restarted by calling open().

Writing a simple running Tracker subclass is straightforward, and can be illustrated by IRBeaconTracker in the package org.mobicomp.ir in the common branch of the src tree. This Tracker is used together with the IrBeacon class, itself also a Tracker]] in the same package.

These two classes exchange messages using a simple protocol. The IRBeaconTracker attempts to make an IrDA connection to another device. When it discovers another IrDA device, it sends its own device identifier and waits for a reply. If the other device is an IRBeacon, the content of the response message is typically a URL.

The IRBeaconTracker constructor first initialises its parent class, setting the run interval at two seconds:

super ( 2000 )

Next, it performs two required actions, setting the descriptive text and adding the predicate name of the ContextElement to the list of elements that it produces:

 description = DESC;
 addElement ( BeaconListener.BEACON );

Finally, it starts the Tracker thread:

open();

The open method in the superclass also registers the Tracker with the ContextService using the the information provided in the description and element list.

A running Tracker subclass should implement the method

protected void checkStatus()

This is a callback from the thread run loop. Here, the subclass should check its input(s), sensor(s), etc. and, if necessary pass ContextElements to the ContextService. In the case of the IRBeaconTracker, we attempt to open an IrDA link and send the device identifier. If this fails, an IOException is thrown and the method returns. If a connection is made, the response message is read. We then create a ContextObject and put it into the store:

 ContextObject obj = new ContextObject ( BeaconListener.BEACON );
 obj.addElement ( BeaconListener.TYPE, IRBeacon.IR );
 try {
     URL surl = new URL ( content );
     obj.addElement ( BeaconListener.URL, content );
 } catch ( MalformedURLException m ) {
     obj.addElement ( BeaconListener.CONTENT, content );
 }
 putContext ( BeaconListener.BEACON, obj, 0, ContextElement.PUBLIC );

The try-catch block tests if the message content is a valid URL. If so we add a url element to the ContextObject, otherwise a content element. The ContextObject is assembled in a way analagous to creating an XML DOM fragment, but using the addElement method of the ContextObject which takes the element name and value as String parameters. Finally, the Tracker putContext method is used to put a beacon event into the store.

There are three versions of the Tracker putContext method:

protected void putContext ( String pred, Object obj, String priv )
protected void putContext ( String pred, Object obj, int ttl, String priv )
protected void putContext ( String subj, String pred, Object obj, int ttl, String priv )

The parameters are:

  • subj: the ContextElement subject (a ContextEntity) identifier.
  • pred: the ContextElement predicate name.
  • obj: the ContextElement object (typically a java.lang.String or a ContextObject).
  • ttl: ContextElement time to live (period of validity) in seconds.
  • priv: one of ContextElement.PRIVATE, ContextElement.LOCAL, ContextElement.PUBLIC.

The first two forms are used to assert context elements referring to the device on which the software is running. The entity (subject) id is supplied automatically when the element is placed in the store. The third form, with subj parameter, allows context to be asserted about a third party, typically a passive entity. Here, the value supplied in the subj parameter is the entity (subject) id of the entity to which the element refers.