Friday, March 6, 2009

SMS Remote Control for Android Apps

I wanted to add an remote control feature to the NoiseAlert application for Android, where menu options could be triggered remotely by sending an SMS to the phone. SMS messages should be delivered to the application only if it is running and the commands should be executed by the foreground activity.

Instead of registering a BroadcastReceiver globally in the AndroidManifest.xml file, the following object can dynamically register and unregister itself for receiving all SMS during the time it is active. All incoming SMS are passed to the objects onReceive method, encoded as an Intent in slightly obscure ways:
public class SmsRemote  extends BroadcastReceiver {
Boolean mActive = false;
Context mContext;

@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle == null) return;

Object pdus[] = (Object[]) bundle.get("pdus");
for (int n = 0; n < pdus.length; n++) {
SmsMessage message = SmsMessage.createFromPdu((byte[]) pdus[n]);

String msg = message.getDisplayMessageBody();
/* check if text of SMS matches remote control command
* and trigger appropriate action.
*/
}
}

public void register(Context context) {
mContext = context;
if(mActive) return;
IntentFilter smsFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
context.registerReceiver(this, smsFilter);
mActive = true;
};

public void deregister() {
if (!mActive) return;
mContext.unregisterReceiver(this);
mActive = false;
}
}

The context is provided by the foreground Activity which can also provide a callback to execute the commands which are to triggered by the SMS. Permission to intercept incoming SMS still needs to be requested in the AndroidManifest.xml file:
<uses-permission android:name="android.permission.RECEIVE_SMS" />