The client-side software of MANES (the MANES client) functions as a virtual interface on each host device. On Android,
ManesService
,
responsible for communicating with the MANES server
and collecting and reporting the data used for
topology estimation.
ManesService
through ManesInterface
.
ManesInterface.send(byte[]
data)
broadcasts a packet to all
one-hop neighbors.
ManesInterface.receive(long
timeout)
returns a new broadcast
packet, if one arrives before the specified
timeout.
Email questions to the MANES listserv, manes-users@umich.edu. To subscribe to the listserv, send an email with the subject "SUBSCRIBE" to manes-users-requests@umich.edu.
MANES requires (via its dependence on C2DM):
ManesService
,
provided by
the ManesClient
application, must be installed on any device that
runs an application using MANES. It functions as a
virtual interface on the host device, sending and
receiving packets over MANES.
Currently ManesClient is provided as an unsigned .apk file. To install it on your device, please first enable installing unsigned packets by clicking on settings->applications->unknown sources. Then open an adb shell and simply type "adb install MANESClient-current.apk".
Note: The following instructions assume some familarity with Android application development in the Eclipse IDE.
Add maneslib.jar to your
classpath. It
provides ManesInterface
,
the interface for sending and receiving packets with
MANES.
Specify ManesService
in
the AndroidManifest.xml
.
<application> <service android:enabled="true" android:name="org.whispercomm.manes.client.macentity.network.ManesService" android:exported="true" android:process=":remote" /> </application>
ManesInterface
object:
Construct a new ManesInterface
using
the ManesInterface(int
appId, Context context)
constructor.
appId
is
a unique
identifier consistent across all instances of
the application. As with
an Ethernet
protocol number or
IP protocol number, it is used to identify to
which handler (or application, in our case) incoming
packets should be
delivered. ManesInteface.receive()
will only receive incoming packets that were sent
with the same appId
.
Like IANA does for Ethernet and IP, we maintain a registry of registered applications ids for MANES. To register an id, please send an email to drbild@umich.edu.
context
is the
Android Context
of your application. For a single-activity application,
the context is just the
Activity
.
During
construction, ManesInterface
binds the provided context
to ManesService
. Construction
fails if
the MANESClient
application that
provides ManesService
is not installed.
import org.whispercomm.manes.client.ManesInterface; public class MyActivity extends Activity { private static final int APP_ID = 1234; private ManesInterface manes; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { manes = new ManesInterface(APP_ID, this); } catch (RemoteException e) { // Handle the failure in a way appropriate for the application. } } }
Broadcast data to one-hop neighbors using
ManesInterface.send(byte[]
data)
.
Note that a send may silently fail at any point between the sender and each potential receiver. As with a real wireless ad hoc system, MANES does not guarantee reliable delivery. If needed, reliable transport must be built on top of MANES, like TCP is built on top of IP.
send()
will signal some local configuration errors, e.g.,
failing to communicate
with ManesService
,
by throwing
an IOException
.
byte[] data = new byte[]{1,2,3,4,5,6,7,8,9,10}; try { manes.send(data); } catch (IOException e) { // Handle the failure in a way appropriate for the application. }
Listen for incoming packets
using ManesInterface.receive(int
timeout)
.
timeout
species the maximum number of
milliseconds
that receive()
should block while waiting for a new packet. It will
return null
after timeout
milliseconds if no new packet was received.
receive()
is a blocking call and thus should be called from a
separate thread, not the main application thread. See
the Hello World example
application.
int timeout = 10; try { byte[] received = manes.receive(timeout); if (received != null) { // Process the packet. } else { // timeout was reached before a new packet arrived. } } catch (IOException e) { // Handle the failure in a way appropriate for the application. }
Before exiting, applications must close
the ManesInterface
by
calling ManesInterfece.unregister()
.
This call unregisters the interface
from ManesService
.
manes.unregister();
The following sample application broadcasts the message "Hello, World" to all one-hop neighbors (as estimated by the MANES infrastructure) and displays all incoming "Hello, World" messages. Download the full application source as an Eclipse project or install the compiled package by scanning the QR code.
package org.whispercomm.manes.apps.helloworld; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.Runnable; import android.app.Activity; import android.os.Bundle; import android.os.RemoteException; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.Toast; import org.whispercomm.manes.client.maclib.ManesInterface; /** * A Hello World sample application for MANES. ** This application broadcasts and receives simple "Hello, World!" messages. */ public class ManesHelloWorld extends Activity implements Runnable { private static final String TAG = "ManesHelloWorld"; private static final int APP_ID = 253; // 253 is available for // experimentation private ManesInterface manes; private Thread receiveThread; private volatile boolean isRunning; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); setContentView(layout); // Add button to screen for sending messages Button button = new Button(this); button.setText("Send \"Hello, World!\""); layout.addView(button); // Configure button to send message when clicked button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { ManesHelloWorld.this.sendMessage(); } }); // Register with MANES try { manes = new ManesInterface(APP_ID, this); } catch (RemoteException e) { this.finish(); return; } // Start receive thread receiveThread = new Thread(this); isRunning = true; receiveThread.start(); } /** * Called when the activity is exiting. */ @Override protected void onDestroy() { isRunning = false; // Stop receive thread; if (receiveThread != null) receiveThread.interrupt(); if (manes != null) manes.unregister(); } /** * Broadcasts a message to all one-hop neighbors. */ public void sendMessage() { try { byte[] data = "Hello, World!".getBytes("UTF-8"); manes.send(data); } catch (UnsupportedEncodingException e) { Log.e(TAG, "Failed to encode message. Unsupported charset.", e); } catch (IOException e) { Log.e(TAG, "Error while sending message.", e); } } /** * Receives and displays incoming messages. */ @Override public void run() { while (isRunning) { byte[] data = null; try { data = manes.receive(500); } catch (IOException e1) { Log.e(TAG, e1.getMessage()); continue; } // Timeout after 500 ms, returning null. if (null != data) { // Debug Log.v(TAG, "Received a new packet!"); try { final String msg = new String(data, "UTF-8"); Log.v(TAG, "Received a packet: " + msg); this.runOnUiThread(new Runnable() { @Override public void run() { Log.v(TAG, "Calling Toast.makeText()."); Toast.makeText(getApplicationContext(), String.format("Received: %s", msg), Toast.LENGTH_SHORT).show(); } }); } catch (UnsupportedEncodingException e) { Log.e(TAG, "Could not decode message. Unsupported charset.", e); } catch (RuntimeException e) { Log.e(TAG, "Failed to decode message.", e); } } } } }
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.whispercomm.manes.apps.helloworld" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".ManesHelloWorld" android:label="@string/app_name" android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:enabled="true" android:name="org.whispercomm.manes.client.macentity.network.ManesService" android:exported="true" android:process=":remote"/> </application> </manifest>