La struttura di Android è estremamente modulare: quasi tutti gli eventi vengono gestiti tramite messaggi di broadcast, che vengono letti dai programmi installati sul device.

Ad esempio, alla ricezione di un SMS, viene scatenato un Intent con al suo interno la proprietà action valorizzata a android.provider.Telephony.SMS_RECEIVED: se  sul sistema è presente un programma che può gestirlo, il gli sistema 'passa la palla' (e l'intent).

Nel caso siano presenti più programmi abilitati a gestire l'intent, android li ordina per priorità e passa l'intent a tutti.

Di recente ho sviluppato un piccolo tool per Android che ho chiamato Microspia: in pratica permette di controllare tramite SMS il telefono sul quale viene installato e, alla ricezione di messaggi contenenti comandi da lui riconosciuti, fa in modo che quell'SMS non venga visualizzato nell'applicazione di gestione messaggi del terminale.

Vediamo come ho fatto...


Per prima cosa, è necessario aggiungere al file AndroidManifest.xml nel nostro progetto la definizione dell'Intent Filter, che elegge la nostra applicazione ad essere 'messa in coda' per gestire un determinato intent:





<receiver android:name=".SMSReader" >

<intent-filter android:priority="10000" >

<action android:name="android.provider.Telephony.SMS_RECEIVED" />

</intent-filter>

</receiver>





e aggiungiamo le corrette permissions:




<uses-permission android:name="android.permission.RECEIVE_SMS" >





Definiamo quindi che la classe SMSReader si occuperà di gestire l'intent con action android.provider.Telephony.SMS_RECEIVED, ed avrà priorità 10000.
Specificando il valore priority, andiamo a dire al sistema quale sarà (appunto) la priorità di questo receiver rispetto ad altri (a valore più alto corrisponde priorità maggiore).
Con un valore 10000 avremo la sostanziale sicurezza che il nostro software sia il primo a ricevere l'intent.

Ora dobbiamo definire la classe SMSReader:


public class SMSReader extends BroadcastReceiver {

@Override

public void onReceive(final Context context, Intent intent) {



Bundle bundle = intent.getExtras();



Object messages[] = (Object[]) bundle.get("pdus");



SmsMessage smsMessage[] = new SmsMessage[messages.length];

for (int n = 0; n < messages.length; n++) {

smsMessage[n] = SmsMessage.createFromPdu((byte[]) messages[n]);

}



// Verifico testo messaggio

if (smsMessage[0].getMessageBody().toString().equals([COMANDO DA INTERPRETARE])) {

this.abortBroadcast();



[CODICE DA ESEGUIRE]

}

}

}





La nostra classe estende la classe base BroadcastReceiver (abilitata alla ricezione degli Intent) ed espone il metodo onReceive.
In onReceive per prima cosa estraiamo i dati dall'intent utilizzando il metodo getExtras, poi dai dati ottenuti preleviamo i dati relativi al PDU ("protocol discription unit", formato standard per gli SMS), contenenti i messaggi presenti nell'intent.
Poi cicliamo i messaggi e appoggiamo tutto all'interno di un array di oggetti android.telephony.SmsMessage (smsMessage).

Successivamente diamo per scontato che all'interno dell'intent sia presente un solo messaggio e andiamo a interpretarne il testo pescandolo tramite il metodo getMessageBody sull'elemento zero dell'array.

Se all'interno del testo del messaggio è presente un comando da eseguire, facciamo in modo che l'SMS non sia recapitato al software di messaggistica presente nel telefono.
Per farlo invochiamo il metodo abortBroadcast() che 'ferma' la propagazione dell'intent ad altri programmi presenti nel terminale.
Nei prossimi articoli vedremo come leggere altri dati presenti nell'SMS e come inviare messaggi dalla nostra applicazione.