About Feature API Guide - Java
Reference code
Classes and interfaces used to send About Feature announcments and data
| Class/interface | Description |
|---|---|
| AboutObj | Class that implements the org.alljoyn.About interface. |
| AboutDataListener | Interface that supplies the list of properties required for Announce signal payload and GetAboutData(). |
| AboutIcon | Container class to hold information about an icon. |
| AboutIconObj | Class that implements the org.alljoyn.Icon interface. |
Classes and interfaces used to receive About Feature announcements and data.
| Class/interface | Description |
|---|---|
| AboutListener | Interface implemented by AllJoyn™ users to receive About interface related events. |
| AboutProxy | Get proxy access to the org.alljoyn.About interface. This class enables the user to interact with the remote About BusObject. |
| AboutIconProxy | Helper class that provides access to the org.alljoyn.Icon interface. This class enables the user to interact with the remote AboutIcon BusObject. |
Build an application that uses About Server
The following is the high-level process to build an application that will broadcast an Announce signal. Lines marked with a * are unique to applications using the About Feature.
- Create an interface that represents an AllJoyn™ interface using annotations.
- Add
announced="true"to the@BusInterfaceannotation* - Implement the interface and the
BusObject - Implement an
AboutDataListener* - Create a new
BusAttachment - Connect
- Bind a session port
- Setup security etc.
- Create and register the
BusObject - Create
AboutObjandannounce*
Build an application that uses About Client
The following is the high-level process to build an application that will
receive an org.alljoyn.About.Announce signal. Lines marked with a * are
unique to applications using the About Feature.
- Create and connect
BusAttachment - Implement an
AboutListener* - Register the new
AboutListener* - call
BusAttachment::WhoImplementsmember function to specify interfaces your application is interested in.*
Sample code for sending an Announce signal
Create an interface that represents an AllJoyn interface using annotations
The interface is a collection methods, signals, and properties. The interface can be represented in xml notation but an annotated interface that represents that interface must be created.
For this sample the following xml interface was used.
<interface name='com.example.about.feature.interface.sample' >
<method name='Echo'>
<arg name='out_arg' type='s' direction='in' />
<arg name='return_arg' type='s' direction='out' />
</method>
</interface>
An Java interface annotated so it can be used for AllJoyn. Note the announced
annotation is set to "true". The interface will not become part of the
Announce signal if the announced annotation is not added to the
@BusInterface annotation.
@BusInterface (name = "com.example.about.feature.interface.sample", announced="true")
public interface SampleInterface {
@BusMethod(name = "Echo")
public String echo(String str) throws BusException;
}
Implement the interface and the BusObject
The implementation of the SampleInterface can be done in just a few lines of
code.
public static class SampleService implements SampleInterface, BusObject {
public String echo(String str) {
return str;
}
}
Implement an AboutDataListener
The AboutDataListener interface has two methods getAboutData and
getAnnouncedAboutData.
The method getAnnouncedAboutData is called by the AllJoyn framework to get a
Map where the key is a String and the value is a Variant. The map is
expected to contain only the data fields that are announced.
See About interface data fields. Feilds that have
multiple strings in multiple langauges will always return the language specified
in the DefaultLanguage tag for the announced data.
The getAboutData method is called by the AllJoyn framework when a AboutProxy
object calls AboutProxy.getAboutData() method. The language must be an
IETF language tage specified by RFC 5646. Important if the language parameter
is null or an empty string the about data for the DefaultLanguage should be
returned. If the language specified is not supported thow an
ErrReplyBusException with the Status LANGUAGE_NOT_SUPPORTED. If any
required field is not supplied then throw an ErrReplyBusException with the
Status ABOUT_ABOUTDATA_MISSING_REQUIRED_FIELD.
All strings must be encoded using UTF-8 encoding.
Sample implementation of an AboutDataListener:
public class MyAboutData implements AboutDataListener {
@Override
public Map<String, Variant> getAboutData(String language) throws ErrorReplyBusException {
System.out.println("MyAboutData.getAboutData was called for `"
+ language + "` language.");
Map<String, Variant> aboutData = new HashMap<String, Variant>();
// nonlocalized values
aboutData.put("AppId", new Variant(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}));
aboutData.put("DefaultLanguage", new Variant(new String("en")));
aboutData.put("DeviceId", new Variant(new String(
"93c06771-c725-48c2-b1ff-6a2a59d445b8")));
aboutData.put("ModelNumber", new Variant(new String("A1B2C3")));
aboutData.put("SupportedLanguages", new Variant(new String[] { "en", "es" }));
aboutData.put("DateOfManufacture", new Variant(new String("2014-09-23")));
aboutData.put("SoftwareVersion", new Variant(new String("1.0")));
aboutData.put("AJSoftwareVersion", new Variant(Version.get()));
aboutData.put("HardwareVersion", new Variant(new String("0.1alpha")));
aboutData.put("SupportUrl", new Variant(new String(
"http://www.example.com/support")));
// localized values
// If the language String is null or an empty string we return the default
// language in this case english
if ((language == null) || (language.length() == 0) || language.equals("en")) {
aboutData.put("DeviceName", new Variant(new String("A device name")));
aboutData.put("AppName", new Variant(new String("An application name")));
aboutData.put("Manufacturer", new Variant(new String(
"A mighty manufacturing company")));
aboutData.put("Description",
new Variant( new String("Sample showing the about feature in a service application")));
} else if (language.equals("es")) { // Spanish
aboutData.put("DeviceName", new Variant(new String(
"Un nombre de dispositivo")));
aboutData.put("AppName", new Variant(
new String("Un nombre de aplicación")));
aboutData.put("Manufacturer", new Variant(new String(
"Una empresa de fabricación de poderosos")));
aboutData.put("Description",
new Variant( new String("Muestra que muestra la característica de sobre en una aplicación de servicio")));
} else {
throw new ErrorReplyBusException(Status.LANGUAGE_NOT_SUPPORTED);
}
return aboutData;
}
@Override
public Map<String, Variant> getAnnouncedAboutData() throws ErrorReplyBusException {
System.out.println("MyAboutData.getAnnouncedAboutData was called.");
Map<String, Variant> aboutData = new HashMap<String, Variant>();
aboutData.put("AppId", new Variant(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}));
aboutData.put("DefaultLanguage", new Variant(new String("en")));
aboutData.put("DeviceName", new Variant(new String("A device name")));
aboutData.put("DeviceId", new Variant(new String("93c06771-c725-48c2-b1ff-6a2a59d445b8")));
aboutData.put("AppName", new Variant( new String("An application name")));
aboutData.put("Manufacturer", new Variant(new String("A mighty manufacturing company")));
aboutData.put("ModelNumber", new Variant(new String("A1B2C3")));
return aboutData;
}
}
About interface data fields
| Field name | Required | Announced | Localized | Signature |
|---|---|---|---|---|
AppId |
yes | yes | no | ay |
DefaultLanguage |
yes | yes | no | s |
DeviceName |
no | yes | yes | s |
DeviceId |
yes | yes | no | s |
AppName |
yes | yes | yes | s |
Manufacturer |
yes | yes | yes | s |
ModelNumber |
yes | yes | no | s |
SupportedLanguages |
yes | no | no | as |
Description |
yes | no | yes | s |
DateofManufacture |
no | no | no | s |
SoftwareVersion |
yes | no | no | s |
AJSoftwareVersion |
yes | no | no | s |
HardwareVersion |
no | no | no | s |
SupportUrl |
no | no | no | s |
Fields marked as Announced are part of the Announce signal. If a value is not
announced then you must use the org.alljoyn.About.GetAboutData method to
access those values.
Fields marked as Required must all be supplied to send an Announce signal.
They are required even if the value is not part of the Announce signal.
AppId is a 128-bit UUID (16-bytes) as specified in RFC 4122.
Create a new BusAttachment
Basic setup that all AllJoyn applications must do to announce there interfaces. Depending on the application additional work may be needed to add in security or application life-time management code.
BusAttachment bus;
bus = new BusAttachment("AppName", BusAttachment.RemoteMessage.Receive);
Status status;
status = bus.connect();
if (status != Status.OK) {
return;
}
// Bind a session port
Mutable.ShortValue contactPort = new Mutable.ShortValue(CONTACT_PORT);
SessionOpts sessionOpts = new SessionOpts();
sessionOpts.traffic = SessionOpts.TRAFFIC_MESSAGES;
sessionOpts.isMultipoint = false;
sessionOpts.proximity = SessionOpts.PROXIMITY_ANY;
sessionOpts.transports = SessionOpts.TRANSPORT_ANY;
status = bus.bindSessionPort(contactPort, sessionOpts, new SessionPortListener() {
public boolean acceptSessionJoiner(short sessionPort, String joiner, SessionOpts sessionOpts) {
if (sessionPort == CONTACT_PORT) {
return true;
} else {
return false;
}
}
});
if (status != Status.OK) {
return;
}
Create and Register the BusObject
SampleService mySampleService = new SampleService();
status = bus.registerBusObject(mySampleService, "/example/path");
if (status != Status.OK) {
return;
}
Create AboutObj and announce
AboutObj aboutObj = new AboutObj(bus);
status = aboutObj.announce(contactPort.value, new MyAboutData());
if (status != Status.OK) {
System.out.println("Announce failed " + status.toString());
return;
}
Sample code for receiving an Announce signal
Code that receives an Announce signal will need to create, start, and connect
a BusAttachment the same as the code that sent the Announce signal. The
application that receives the Announce signal does not need to bind a session
port. See Create a new BusAttachment
Implement an AboutListener
static class MyAboutListener implements AboutListener {
public void announced(String busName, int version, short port, AboutObjectDescription[] objectDescriptions, Map<String, Variant> aboutData) {
// Place code here to handle Announce signal.
}
}
The AboutListener is called by the AllJoyn routing node when an Announce
signal is found. The Announced call back contains all the information
contained in the received Announce signal as well as the unique BusName of the
BusAttachment that emitted the Announce signal. This information can be used
to form a session with the remote device; and Make a ProxyBus object based on
the interfaces reported in the objectDescriptions.
Register the new AboutListener and call BusAttachment.whoImplements
````java AboutListener listener = new MyAboutListener(); bus.registerAboutListener(listener);
String ifaces[] = {"com.example.about.feature.interface.sample"}; status = bus.whoImplements(ifaces); if (status != Status.OK) { return; }
#### The whoImplements method
The `whoImplements` method is used to declare your interest in one or more
specific interfaces. If a remote device is announcing the interface(s) then
all Registered `AboutListeners` will be called.
For example, if you need both `com.example.Audio` *and*
`com.example.Video` interfaces then do the following.
Register `AboutListener` once:
```java
String ifaces[] = {"com.example.Audio", "com.example.Video"};
RegisterAboutListener(aboutListener);
bus.whoImplements(ifaces));
If the AboutListener should be called if com.example.Audio or
com.example.Video interfaces are found then call WhoImplements multiple
times:
RegisterAboutListener(aboutListener);
String audioInterface[] = {"com.example.Audio"};
bus.whoImplements(audioInterface);
String videoInterface[] = {"com.example.Video"};
bus.whoImplements(videoInterface);
The interface name may be a prefix followed by a *. Using this, the example
where we are interested in com.example.Audio or com.example.Video
interfaces could be written as:
String exampleInterface[] = {"com.example.*"};
RegisterAboutListener(aboutListener);
WhoImplements(exampleInterface);
The AboutListener will receive any announcement that implements an interface
beginning with the com.example. name.
It is the AboutListeners responsibility to parse through the reported interfaces
to figure out what should be done in response to the Announce signal.
Calls to whoImplements is ref counted. If whoImplements is called with the same
list of interfaces multiple times then cancelWhoImplements must also be called
multiple times with the same list of interfaces.
Specifying null for the interfaces parameter is allowed, however, it
could have significant impact on network performance and should be avoided
unless all announcements are needed.
Add an AboutIcon (optional)
An application that sends an Announce signal can be extended to broadcast a
device icon using an instance of the AboutIconObj class.
Provision for the Icon content and URL
An icon is published directly as a byte array or a reference URL, and is provisioned as follows:
Create an icon using a byte array. An Icon size of 72 pixels x 72 pixels is recommended.
byte[] iconContent = { (byte)0x89, 0x50, 0x4E, 0x47/* Add relevant data here */ };
AboutIcon icon = null;
try {
icon = new AboutIcon("image/png", iconContent);
} catch (BusException e) {
System.out.println("AboutIcon threw a BusException when it was unexpected.");
}
Create an icon using a URL.
AboutIcon icon = null;
try {
icon = new AboutIcon("image/png", "http://www.example.com");
} catch (BusException e) {
System.out.println("AboutIcon threw a BusException when it was unexpected.");
}
As long as the MimeType of the Url and the icon content are the same. Both the Url and icon content can be set.
byte[] iconContent = { (byte)0x89, 0x50, 0x4E, 0x47/* Add relevant data here */ };
AboutIcon icon = null;
try {
icon = new AboutIcon("image/png", "http://www.example.com", iconContent);
} catch (BusException e) {
System.out.println("AboutIcon threw a BusException when it was unexpected.");
}
AboutIconObj
The AboutIconObj will create and register a BusObject to handle remote
method calls made on the org.alljoyn.Icon interface. The AboutIconObj is
announced by default. Applications interested in the org.alljoyn.Icon
interface can call WhoImplements(org.alljoyn.Icon) to find applications
that broadcast device icon information.
Announce the org.alljoyn.Icon interface:
AboutIconObj aio = new AboutIconObj(bus, icon);
AboutObj aboutObj = new AboutObj(bus);
status = aboutObj.announce(PORT_NUMBER, aboutData));
Discover the org.alljoyn.Icon interface
status = clientBus.whoImplements(new String[] {org.alljoyn.bus.ifaces.Icon.INTERFACE_NAME});
Using Ping to determine presence
The BusAttachment Ping method can be used to determine
if a device is responsive. Contents of an org.alljoyn.About.Announce signal can
be stale so it is recommended to ping the device to see if it is still present
and responsive before attempting to form a connection.
NOTE: The BusAttachment.ping method makes a bus call. If BusAttachment.ping
is called inside an AllJoyn callback, BusAttachment.enableConcurrentCallbacks
must be called first.
// pinging a remote bus, wait a max of 5 seconds
bus.enableConcurrentCallbacks();
QStatus status = bus.ping(busName.c_str(), 5000);
if( ER_OK == status) {
// remote device found
...
}
Request non-announced data
If there is a need to request information that is not contained in the announcement, perform the following steps.
- Join the session
Create a session with the application by calling BusAttachment.joinSession.
NOTE: The variables name and port are obtained from the AboutListener::Announced member function.
```java SessionOpts sessionOpts = new SessionOpts(); sessionOpts.traffic = SessionOpts.TRAFFIC_MESSAGES; sessionOpts.isMultipoint = false; sessionOpts.proximity = SessionOpts.PROXIMITY_ANY; sessionOpts.transports = SessionOpts.TRANSPORT_ANY;
Mutable.IntegerValue sessionId = new Mutable.IntegerValue();
bus.enableConcurrentCallbacks();
Status status = bus.joinSession(busName, port, sessionId, sessionOpts, new SessionListener()); if (status != Status.OK) { return; } ```
- Create an
AboutProxy
Generate an About ProxyBusObject by passing the local BusAttachment, the
name of the remote BusAttachment, and the SessionId obtained from the
BusAttachment.joinSession call.
java
AboutProxy aboutProxy = new AboutProxy(mBus, busName, sessionId.value);
try {
Map<String, Variant> aboutData;
aboutData = aboutProxy.getAboutData("en");
} catch (BusException e) {
System.out.println("Unexpected BusException.")
}
- Create
AboutIconProxy(optional)
Generate an Icon ProxyBusObject by passing the local BusAttachment, the
name of the remote BusAttachment, and the SessionId obtained from the
BusAttachment.joinSession call.
```java AboutIconProxy aiProxy = new AboutIconProxy(clientBus, aListener.remoteBusName, sessionPortlistener.sessionId); AboutIcon aIcon; try { aIcon = aiProxy.getAboutIcon(); } catch (BusException e) { System.out.println("Unexpected BusException."); }
// Get the Url String url = aIcon.getUrl(); // Get the content byte[] content = aIcon.getContent(); // Get the MimeType String mimeType = aIcon.getMimeType(); ```