[General Topics] KaterinaDekha:
Author: Apriorit (Driver Development Team)

This article is useful for you if you want to develop your own SMS (or another service) handler. As a sample, I choose received SMS handler that receives SMS, encrypts them, and puts into the SMS table of the Android system database.

Android Manifest

Manifest is a very important part of an Android application. You can find everything about the Android manifest by this link.

And now I’ll try to describe every line that is important for us.

The first are permissions . The application must receive, write and read SMS from the database. Permissions can be obtained as follows:

1.<uses-permission android:name="android.permission.WRITE_SMS" />
2.<uses-permission android:name="android.permission.READ_SMS" />
3.<uses-permission android:name="android.permission.RECEIVE_SMS" />

Now it’s time to write a standard activity starting. This is a part of an application that will show us SMS and will decrypt encrypted ones. There is nothing special:

1.<activity android:name=".SecureMessagesActivity" android:label="@string/app_name" >
2. <intent-filter>
3. <action android:name="android.intent.action.MAIN" />
4. <category android:name="android.intent.category.LAUNCHER" />
5. </intent-filter>
6.</activity>

And now we need to catch all received SMS into our SMS receiver:

1. <receiver android:name=".SmsReceiver" android:exported="true" >
2. <intent-filter android:priority="999">
3. <action android:name="android.provider.Telephony.SMS_RECEIVED" />
4. </intent-filter>
5. </receiver>

Where

android:exported

indicates that the SmsReceiver class must receive event not only from the application but also from the whole Android system.

android:priority=”999”

indicates that receiver has the highest priority and will catch the SMS event before the system. Be careful with this because incorrect work of your receiver can corrupt the system or important data in your device. You can read about priority values here.

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

indicates that we want to get received SMS.

View Model

The project contains only one XML layout. There is one button and one list. The button is used for getting inbox SMS from the system and the list is used for showing messages.

Here is the XML layout code:

01.<?xml version="1.0" encoding="utf-8"?>
02.<LinearLayout xmlns:android="'link to schemas(dot)android(dot)com/apk/res/android'"
03. android:orientation="vertical" android:layout_width="fill_parent"
04. android:layout_height="fill_parent" android:id="@+id/MainLayout"
05. android:background="@android:color/background_light">
06.
07. <Button android:layout_height="wrap_content"
08. android:layout_width="match_parent" android:id="@+id/UpdateList"
09. android:layout_margin="2dip"
10. android:text="Update SMS list" />
11.
12. <ListView android:id="@+id/SMSList"
13. android:layout_height="wrap_content"
14. android:layout_width="match_parent"
15. android:layout_margin="2dip" />
16.
17.</LinearLayout>

Listeners will be described later.

Encryption/Decryption

All incoming SMS will be encrypted. For this reason, the class, which provides encrypting and decrypting, is added.

I use the AES algorithm and standard Android libraries. You can use this class for your own purposes.

String encrypt( String password, String data ) – encrypts string where the key will be generated using a password string. The returned value is a Base64 string that was previously encrypted.

01.// Encrypts string and encodes in Base64
02.public static String encrypt( String password, String data ) throws Exception
03.{
04. byte[] secretKey = generateKey( password.getBytes() );
05. byte[] clear = data.getBytes();
06.
07. SecretKeySpec secretKeySpec = new SecretKeySpec( secretKey, CIPHER_ALGORITHM );
08. Cipher cipher = Cipher.getInstance( CIPHER_ALGORITHM );
09. cipher.init( Cipher.ENCRYPT_MODE, secretKeySpec );
10.
11. byte[] encrypted = cipher.doFinal( clear );
12. String encryptedString = Base64.encodeToString( encrypted, Base64.DEFAULT );
13.
14. return encryptedString;
15.}

String decrypt( String password, String encryptedData ) – decrypts a Base64 string with a password string.

01.// Decrypts string encoded in Base64
02.public static String decrypt( String password, String encryptedData ) throws Exception
03.{
04. byte[] secretKey = generateKey( password.getBytes() );
05.
06. SecretKeySpec secretKeySpec = new SecretKeySpec( secretKey, CIPHER_ALGORITHM );
07. Cipher cipher = Cipher.getInstance( CIPHER_ALGORITHM );
08. cipher.init( Cipher.DECRYPT_MODE, secretKeySpec );
09.
10. byte[] encrypted = Base64.decode( encryptedData, Base64.DEFAULT );
11. byte[] decrypted = cipher.doFinal( encrypted );
12.
13. return new String( decrypted );
14.}

byte[] generateKey( byte[] seed ) – generates a secret key using a seed (String.getBytes() in our case). A secret key is a byte array generated for the specific encryption algorithm.

01.public static byte[] generateKey( byte[] seed ) throws Exception
02.{
03. KeyGenerator keyGenerator = KeyGenerator.getInstance( CIPHER_ALGORITHM );
04. SecureRandom secureRandom = SecureRandom.getInstance( RANDOM_GENERATOR_ALGORITHM );
05. secureRandom.setSeed( seed );
06. keyGenerator.init( RANDOM_KEY_SIZE, secureRandom );
07. SecretKey secretKey = keyGenerator.generateKey();
08. return secretKey.getEncoded();
09.}

Handle Received SMS

The main class that receives the SMS is SmsReceiver. It extends BroadcastReceiver class. This is the main concept of any Android service or receiver. Any child of BroadcastReceiver must contain the onReceive method, which receives Context and Intent parameters. You can find all additional information on the Android developer documentation site.

So, we get event and go into the onReceive method. The first line is:

Bundle extras = intent.getExtras();

The Bundle object is a simple map. It contains pairs of keys and values. SMS are placed in this bundle. The key of SMS is pdus:

1.public static final String SMS_EXTRA_NAME =”pdus”;
2.…
3.Object[] smsExtra = (Object[]) extras.get( SMS_EXTRA_NAME );

After this, the smsExtra value contains arrays of bytes. Here is a full example:

01.public void onReceive( Context context, Intent intent )
02. {
03. // Get the SMS map from Intent
04. Bundle extras = intent.getExtras();
05.
06. String messages = "";
07.
08. if ( extras != null )
09. {
10. // Get received SMS array
11. Object[] smsExtra = (Object[]) extras.get( SMS_EXTRA_NAME );
12.
13. // Get ContentResolver object for pushing encrypted SMS to the incoming folder
14. ContentResolver contentResolver = context.getContentResolver();
15.
16. for ( int i = 0; i < smsExtra.length; ++i )
17. {
18. SmsMessage sms = SmsMessage.createFromPdu((byte[])smsExtra[i]);
19.
20. String body = sms.getMessageBody().toString();
21. String address = sms.getOriginatingAddress();
22.
23. messages += "SMS from " + address + " :\n";
24. messages += body + "\n";
25.
26. // Here you can add any your code to work with incoming SMS
27. // I added encrypting of all received SMS
28.
29. putSmsToDatabase( contentResolver, sms );
30. }
31.
32. // Display SMS message
33. Toast.makeText( context, messages, Toast.LENGTH_SHORT ).show();
34. }
35.
36. // WARNING!!!
37. // If you uncomment the next line then received SMS will not be put to incoming.
38. // Be careful!
39. // this.abortBroadcast();
40. }

So, when the SMS list gets into smsExtra, then SMS should be parsed. And the method createFromPdu from SmsMessage class should be called for this. I do not write about the methods and fields of SmsMessage class because it is documented enough.

So, SmsReceiver gets the SMS and now can do anything with it. In the example, SMS are encrypted and put into the SMS table of the device database. I need this to allow the encrypted SMS viewing by the default Android SMS viewer.

Of course, the PDU (Protocol Description Unit) can be parsed and generated manually, but its format can be changed anytime by Google. If you are interested in researching the PDU format, you can analyze it at Android sources: for CDMA phones and for GSM phones.

When SMS is got, it is shown using the Toast class. And I want to tell you about the last commented line:

// this.abortBroadcast();

I commented it because it’s dangerous. As you remember, the receiver catches SMS events before the system works with it. So, the abortBroadcast method stops the SMS dispatching to other receivers. If something is going wrong, the SMS will not be saved.

You can see the full example at the attached sources.

Read and Decrypt SMS

I want to tell you why the activity is needed. Below the click listener for the button on the main screen is listed. In the following code, all SMS that are placed in Inbox are read and then the sender information and SMS text are put into the list:

01.public void onClick( View v )
02.{
03. ContentResolver contentResolver = getContentResolver();
04. Cursor cursor = contentResolver.query( Uri.parse( "content://sms/inbox" ), null, null, null, null);
05.
06. int indexBody = cursor.getColumnIndex( SmsReceiver.BODY );
07. int indexAddr = cursor.getColumnIndex( SmsReceiver.ADDRESS );
08.
09. if ( indexBody < 0 || !cursor.moveToFirst() ) return;
10.
11. smsList.clear();
12.
13. do
14. {
15. String str = "Sender: " + cursor.getString( indexAddr ) + "\n" + cursor.getString( indexBody );
16. smsList.add( str );
17. }
18. while( cursor.moveToNext() );
19.
20. ListView smsListView = (ListView) findViewById( R.id.SMSList );
21. smsListView.setAdapter( new ArrayAdapter<String>( this, android.R.layout.simple_list_item_1, smsList) );
22. smsListView.setOnItemClickListener( this );
23.}

The SMS text is obtained from the list, decrypted and then shown. SmsReceiver.PASSWORD can be changed in any way. The list item listener is as follows:

01.public void onItemClick( AdapterView<?> parent, View view, int pos, long id )
02. {
03. try
04. {
05. String sender = smsList.get( pos ).split("\n")[0];
06. String encryptedData = smsList.get( pos ).split("\n")[1];
07. String data = sender + "\n" + StringCryptor.decrypt( new String(SmsReceiver.PASSWORD), encryptedData );
08. Toast.makeText( this, data, Toast.LENGTH_SHORT ).show();
09. }
10. catch (Exception e)
11. {
12. e.printStackTrace();
13. }
14. }