The client-side software of MANES (the MANES client) functions as a virtual interface on each host device. On Android,

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.

System Requirements

MANES requires (via its dependence on C2DM):

  • Android 2.2 or higher with the Market application installed, and
  • a Google user account registered on the phone.

Installing ManesService

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".

ManesClient.apk Download QR Code
Scan with Android phone to install.

Creating an activity that uses MANES

Note: The following instructions assume some familarity with Android application development in the Eclipse IDE.

Including the MANES client library:

Add maneslib.jar to your classpath. It provides ManesInterface, the interface for sending and receiving packets with MANES.

images/manesjar-in-eclipse-570.png
Adding maneslib.jar to build path in Eclipse.
Configuring the Android manifest:
<application>
<service
    android:enabled="true"
    android:name="org.whispercomm.manes.client.macentity.network.ManesService"
    android:exported="true"
    android:process=":remote" />
</application>
Getting a 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.
        }
    }
}
Broadcasting a packet:

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.
}
Listening for packets:

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.
}
Unregistering with MANES:

Before exiting, applications must close the ManesInterface by calling ManesInterfece.unregister(). This call unregisters the interface from ManesService.

manes.unregister();

Hello World Sample Application

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.

manes-helloworld.apk Download QR Code
Scan with Android phone to install.
org/whispercomm/manes/apps/helloworld/ManesHelloWorld.java
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); } } } } }

org/whispercomm/manes/apps/helloworld/AndroidManifest.xml
<?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>