PhoneFinder - SMS Phone Locator
Introduction
In this tutorial we will create an application called PhoneFinder. This application will illustrate how to deal with sending and receiving SMS messages. The idea of the application is that when your phone is lost or stolen you will be able to use someone else's phone to retrieve the GPS coordinates at your phone's location to help you find it.
This application needs an Activity that will allow the user to enter in the password and an IntentReceiver that will be kicked off on incoming SMS messages.
Click here to download the complete source.
Password Entry
We will use the simple dialog shown below for password entry. Once the password is correctly entered we will save a MD5 sum of the password into the SharedPreferences for the package. The preferences is an easy way to save small amounts of persistent data. It is also only accessible by classes in your package. We will take the extra precaution of saving an MD5 of the password, this way if the data was read somehow it would not reveal the plain text password unless the password is very weak (aka in the dictionary).

The layout for this dialog, main.xml, is shown below:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/password_label"
- />
- <EditText android:id="@+id/password"
- android:maxLines="1"
- android:layout_marginTop="2dip"
- android:layout_width="wrap_content"
- android:ems="25"
- android:layout_height="wrap_content"
- android:autoText="true"
- android:scrollHorizontally="true"
- android:password="true" />
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/password_confirm_label"
- />
- <EditText android:id="@+id/password_confirm"
- android:maxLines="1"
- android:layout_marginTop="2dip"
- android:layout_width="wrap_content"
- android:ems="25"
- android:layout_height="wrap_content"
- android:autoText="true"
- android:scrollHorizontally="true"
- android:password="true" />
- <Button android:id="@+id/ok"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:text="@string/button_ok" />
- <TextView android:id="@+id/text1"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- />
- </LinearLayout>
As you can see it is a very simple layout, 2 text fields, 2 input fields, a button and another text field at the end to display messages to the user. The strings are defined in the strings.xml for better multilingual support.
The code for this activity is very simple. It's job is to make sure that the password is at least 6 characters, and that the 2 password fields match. Once that is confirmed then all we need to do is save the MD5 sum of the password the user entered into the SharedPreferences.
Here is the PhoneFinder Activity:
- public class PhoneFinder extends Activity {
- private TextView messages;
- private EditText pass1;
- private EditText pass2;
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.main);
- messages = (TextView) findViewById(R.id.text1);
- pass1 = (EditText) findViewById(R.id.password);
- pass2 = (EditText) findViewById(R.id.password_confirm);
- button.setOnClickListener(clickListener);
- }
- private OnClickListener clickListener = new OnClickListener() {
- if (p1.equals(p2)) {
- if (p1.length() >= 6 || p2.length() >= 6) {
- Editor passwdfile = getSharedPreferences(PhoneFinder.PASSWORD_PREF_KEY, 0).edit();
- passwdfile.putString(PhoneFinder.PASSWORD_PREF_KEY,
- md5hash);
- passwdfile.commit();
- messages.setText("Password updated!");
- } else
- messages.setText("Passwords must be at least 6 characters");
- } else {
- pass1.setText("");
- pass2.setText("");
- messages.setText("Passwords do not match");
- }
- }
- };
- }
In onCreate() we initialize the various Views that we are using in the layout and then we setup the OnClickListener object for the "ok" button. When the "ok" button is pressed we are taken down into the onClick() function that starts on line 40.
In the onClick() function we confirm that the requirements for password length is met, and we make sure that both of the text boxes match. If that all happens then we get to line 48 where we setup the SharedPreferences.Editor class. This class allows us to edit the shared preferences for this application. It is called "shared" because it is application wide preferences, there are also Activty level preferences available via Activity.getPreferences(int).
Writing to the preferences is easy once you have the Editor object. You just use one of the putX() functions to add key/value pairs and then call the commit() function to save the results.
Then on line 48 you'll see that we call the member function getMd5Hash(String) which returns the MD5 sum as a string, and we store that in the preferences. Here is the getMd5Hash(String) function:
- try {
- byte[] messageDigest = md.digest(input.getBytes());
- while (md5.length() < 32)
- md5 = "0" + md5;
- return md5;
- Log.e("MD5", e.getMessage());
- return null;
- }
- }
We use a android.security.MessageDigest object passing in "MD5" as the algorithm we want to use. The digest() function is called passing in a Byte array from the String passed in, and it returns a byte array. This byte array can then be saved as a BigInteger and then converted to a hex string using toString(16). When we convert this byte array to a BigInteger leading zeros will be trimmed, so we add leading zeros with the while loop until the MD5 sum reaches 32 characters.
On the next page we will create the IntentReciever that will listen for SMS messages and respond to a relavent SMS message...
<!--pagebreak-->
Handling the SMS Message
Now that the user can save the password to the preferences we will need to check all incoming SMS messages and respond to any relavent ones. We are looking for a message in the format:
SMSLOCATE:<password>
So if a SMS messages starts with "SMSLOCATE:" and the MD5 sum of the password after the ":" matches that of the one saved earlier then we will send a text message with everything we know about the phones current location. To do this we need to setup an IntentReceiver that will respond to the "android.provider.Telephony.SMS_RECEIVED" action, this will take some additional lines in the AndroidManifest.xml file. Here is the AndroidManifest.xml file:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.helloandroid.android.phonefinder">
- <uses-permission android:name="android.permission.RECEIVE_SMS" />
- <uses-permission android:name="android.permission.ACCESS_GPS" />
- <uses-permission android:name="android.permission.ACCESS_LOCATION" />
- <application android:icon="@drawable/icon">
- <activity android:name=".PhoneFinder" android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <receiver android:name=".FinderReceiver">
- <intent-filter>
- <action android:name="android.provider.Telephony.SMS_RECEIVED" />
- </intent-filter>
- </receiver>
- </application>
- </manifest>
You'll see starting on line 4 that our application must request permission to receive SMS messages, Access the GPS device, and Access the phone's location. This is requested with the <uses-permission> tag. Then down on line 15 we must specify our receiver as "FinderReceiver" and also the intent-filter that will be checked against all intents that are broadcasted. You can see here we are only concerned with IntentBroadcasts with the action "android.provider.Telephony.SMS_RECEIVED".
Now the OS will know what receiver to call for that action, so lets create the IntentReceiver called FinderReceiver:
- public class FinderReceiver extends IntentReceiver {
- @Override
- SharedPreferences passwdfile = context.getSharedPreferences(
- PhoneFinder.PASSWORD_PREF_KEY, 0);
- null);
- if (correctMd5 != null) {
- SmsMessage[] messages = Telephony.Sms.Intents
- .getMessagesFromIntent(intent);
- for (SmsMessage msg : messages) {
- if (msg.getMessageBody().contains("SMSLOCATE:")) {
- if (tokens.length >= 2) {
- if (md5hash.equals(correctMd5)) {
- LocationManager lm =
- SmsManager sm = SmsManager.getDefault();
- sm.sendTextMessage(to, null, lm.getCurrentLocation("gps").toString(),
- null, null, null);
- Toast.makeText(context, context.getResources().getString(R.string.notify_text) + to,
- Toast.LENGTH_SHORT).show();
- }
- }
- }
- }
- }
- }
- }
We start by getting the correct MD5 sum for the saved password from the SharedPreferences, we do this on lines 18-21. If there is actually a password in there then we now want to loop through all of the SMS messages that were received.
We use Telphony.Sms.Intents.getMessageFromInent(intent) to get an array of SmsMessages. We will now loop through this array and see if the body of the message has "SMSLOCATE:" in it. If a message does, we need to get the password after the ":", take it's MD5 sum and compare it to the one we are looking for.
If the passwords match, then we get into the block starting on line 36. Now all we need is a String with the address to send it to, and a String with the location information. To get the location information we create a new LocationManager and simply use getCurrentLocation("gps").toString(). This will print out everything known about the location using gps as the location provider. We then send the text message using an SmsManager object. We also show a notification (Toast) saying that the message was sent.
Note: It might be a good idea to have an option in the password entry dialog to either show or don't show the notification. If the phone was stolen it would be better to hide the notification or the theif may realize that he's being tracked and will turn off the phone, etc.
Testing this operation
Now, everything is setup. With the new version of the SDK it is very easy to send in phone calls or text messages to the emulator. It is all done using the "Emulator Control" view in Eclipse. You can add this view by going to "Window -> Show View -> Other" and then selecting the "Emulator Control" in the Android section.
So to test first you need to launch the main activity and setup a password. For this example I've entered "123456" for the password. Now you can send a text message with "SMSLOCATE:123456" as the body of the text as shown below:

And then after you send it you should see a notification like this:

There you have it! I think this is a great example of how easy it is to develop useful applications for Android. Two basic objects to handle a very useful task.

Comments
iphone
thanks for sharing
Blackberry Application Developer
Android Applications Development
Market iPhone Application
Blackberry Application Consulting
Cell Phone Product Development
i really like this
i love sms phone finder... i will try the source.
sticker printing-folder printing-static clings-bumper sticker printing-cheap sticker printing
u like me
While Comcast and industry links of london groups want us to think that their Links of London Charms mega-media merger is good for business links of london jewellery, we’re reminding everyone that it’s a bad deal for consumers links of london sale.
How it works
Hello ,
can anyone explain me how this works.(Workflow)
Tutorial no longer working
The latest Android SDK complains about the following:
android.security package doesn't exist
The classes android.content.IntentReceiver and android.provider.Telephony don't exist :(
sms emulator to emulator
Thanks for the tutorial.. that's great! :)
Then, how to send sms from an android emulator to the other android emulator besides from console/emulator control? Is there any "default" phone number for each instance of android emulator?
Can i use this on a
Can i use this on a satellite phone?
Great
I am waiting for Android phones to begin to developp on it
My french Blog on Android
http://gphone.news.free.fr
I checked out your blog and
I checked out your blog and a lot of great info there.
GPS to GPS
Isn't there a way for the GPS or Wifi in another phone contact the GPS on your phone and track its location. even put it on a map.
Then you wouldn't need to send a text message and you could track it on a map.
How would this be done?
Yeah, you could definitely
Yeah, you could definitely do that using GTalk (XMPP) communication between the two phones and then putting the location on a Map with directions. It's outside the scope of this tutorial but would be a great feature!
where is the sms stored in
where is the sms stored in the receiving end?
to get the location name from statusbar notification
how to get the location name from my statusbar notifiacation and also how we know the service is currently running or not? and how to stop the service
Stopping the service
Api demo s there for starting and stopping of service......
There is no service running,
There is no service running, it's simply a listener that will be "woken up" whenever there is an SMS coming in.
Suggestion on auto-start app
Thanks for the tutorial. Your app would be very useful if phone is ever lost or stolen. To make this app truly robust, I recommend having it auto-start whenever the app turns on. You will need to have the app listen for android.intent.action.BOOT_COMPLETED_ACTION
How about that?
Not necessary because the
Not necessary because the Intent will be broadcasted everytime an SMS is sent, and the IntentReceiver will be woken up even if it's not running.
SMS message
After launching the application in the emulator, I try to send an SMS via the Emulator Control. But for some reason the Telephony Actions are grayed out(disabled). How can I fix this?
Sometimes the emulator gets
Sometimes the emulator gets disconnected from Eclipse and you'll need to close the emulator and kill the "adb" process. Then try launching again. Also, some people have suggested using ddms to do this type of thing. DDMS can be started by executing the ddms command in the tools directory. Click here for some more information on eclipse and the emulator getting disconnected.