How to Send an SMS from Android

May 19, 2016
Written by

JINafM6SS1NtGL53oKkDfSJ800s3OsIa6faNfk8J6KbF_ZG68W6vf1cymXgWcfCNZYmmvmsGn7zxgbRXeaVYHzeXyB_sPuMUI-uycPjl8CKsvabkr6yZnCYfDaZmU1uuONblkonu

We talk a lot about how to send SMS Messages from web applications, but how to send an SMS message from Android? There are a couple of extra considerations for that. Still, we’ll be have it shipped in 15 minutes. Let’s go!

The Problem

While Twilio is a REST API and theoretically you could make an HTTP request to it directly, you would need to store your Twilio credentials inside your app which poses a serious security issue. An attacker could decompile the application, extract your credentials and use your Twilio account for anything they liked.

To avoid this we will create a backend application that implements the Twilio REST API, wraps up your credentials and sends SMS messages for you. Then you can call your backend application from your Android application and send SMS messages without distributing your credentials.

Our tools

For our app to be able to send a text message using the Twilio REST API we will need the following:

If you just want to skip to coding your mobile app, feel free clone this repository with the backend, or just deploy it to Heroku.

button.png

You will also find the entire Android application in this repository.

Creating our backend

Because I’ll be using Java to create this backend, I’m gonna start by opening IntelliJ IDEA and creating a new Java project there. We wrote a Getting Started with Gradle and the Spark Framework tutorial in the past in case you need help setting things up.

NewGradleProject.gif

Now that our project is created, open build.gradle and add the following plugins and dependencies to it.


group 'uk.co.placona'
version '1.0-SNAPSHOT'

buildscript {
    repositories { jcenter() }
    dependencies {
        classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3'
    }
}

apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'

sourceCompatibility = 1.8
mainClassName = 'SMSBackend'

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
    compile 'com.sparkjava:spark-core:2.6.0'
    compile group: 'com.twilio.sdk', name: 'twilio', version: '7.9.0'
    compile 'org.slf4j:slf4j-simple:1.6.1'
}

Create a new class in SMSMobileBackend/src/main/java called SMSBackend and add a Main method to it.


import static spark.Spark.*;

public class SMSBackend {
   public static void main(String[] args) {
       get("/", (req, res) -> "Hello, World!");
   }
}

Run the application by opening the Gradle tab, double-clicking shadowJar and running the generated jar file in SMSMobileBackend/build/libs.

run-app.gif

When you load up http://127.0.0.1:4567 on your browser you should see a hello world message.

Let’s make sure this application is also available externally with the help of ngrok so our mobile app can get access to it. In your terminal run:

ngrok http 4567


Now that you know your setup is correct and copied the URL ngrok generated for you, go back to the SMSBackend class and create a new endpoint to handle sending SMS messages. Our android application will then be able send requests to it. Your class should end up like this:

import com.twilio.http.TwilioRestClient;
import com.twilio.rest.api.v2010.account.Message;
import com.twilio.rest.api.v2010.account.MessageCreator;
import com.twilio.type.PhoneNumber;
import spark.Spark;

import static spark.Spark.get;
import static spark.Spark.post;

public class SMSBackend {
   public static void main(String[] args) {
       get("/", (req, res) -> "Hello, World");

        TwilioRestClient client = new TwilioRestClient.Builder(System.getenv("TWILIO_ACCOUNT_SID"), System.getenv("TWILIO_AUTH_TOKEN")).build();

        post("/sms", (req, res) -> {
            String body = req.queryParams("Body");
            String to = req.queryParams("To");
            String from = YOUR_TWILIO_PHONE_NUMBER;

            Message message = new MessageCreator(
                    new PhoneNumber(to),
                    new PhoneNumber(from),
                    body).create(client);

            return message.getSid();
        });
   }
}

Just make sure you replace the Account Sid, Auth Token, and Twilio phone number with the ones you can get from the dashboard. Restart the application and we should be all set with this.

Creating our App

Now that we have our backend running head to Android Studio or your favourite Android IDE and create a new project called Twilio SMS.

twilio-sms-android.gif

When the project is finished loading, open the app’s build.gradle and add the dependency OkHttp to it. OkHttp is an open source project by Square that makes it really easy for us to make an HTTP request to our Java backend.


dependencies {
   compile fileTree(dir: 'libs', include: ['*.jar'])
   testCompile 'junit:junit:4.12'
   compile 'com.android.support:appcompat-v7:23.3.0'
   compile 'com.squareup.okhttp3:okhttp:3.2.0'
}

When you save the file, your IDE will ask you to sync the dependencies of the project. Go ahead and let it do its thing. Once it completes, you will have the dependency downloaded.

Open AndroidManifest.xml and add the Internet permission to your app. We will need that to make HTTP requests to our backend app.


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="uk.co.placona.twiliosms">

   <uses-permission android:name="android.permission.INTERNET"/>

Let’s create our layout by opening res/layout/activity_main.xml and add two text fields for telephone and message, and a button to send it.


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context="uk.co.placona.twiliosms.MainActivity">

   <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Phone Number"
       android:id="@ id/textView"
       android:layout_alignParentTop="true"
       android:layout_alignParentLeft="true"
       android:layout_alignParentStart="true" />

   <EditText
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:id="@ id/txtNumber"
       android:hint=" 44 12345 67890"
       android:layout_below="@ id/textView"
       android:layout_alignParentLeft="true"
       android:layout_alignParentStart="true"
       android:layout_alignParentRight="true"
       android:layout_alignParentEnd="true" />

   <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Message"
       android:id="@ id/textView2"
       android:layout_below="@ id/txtNumber"
       android:layout_alignParentLeft="true"
       android:layout_alignParentStart="true" />

   <EditText
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:inputType="textMultiLine"
       android:ems="10"
       android:id="@ id/txtMessage"
       android:layout_below="@ id/textView2"
       android:layout_alignParentLeft="true"
       android:layout_alignParentStart="true"
       android:layout_alignRight="@ id/txtNumber"
       android:layout_alignEnd="@ id/txtNumber" />

   <Button
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Send"
       android:id="@ id/btnSend"
       android:layout_centerVertical="true"
       android:layout_alignRight="@ id/txtMessage"
       android:layout_alignEnd="@ id/txtMessage" />
  
</RelativeLayout>

If you run the application now, you will see that your app looks like this:

Screenshot_20160509-143412.png

If you enter a message and hit Send however, you will notice that nothing happens. That’s because we’re still not making a request to our backend. Let’s do this now.

Open MainActivity.java and add the following member variables and imports to the top of the class:


package uk.co.placona.twiliosms;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity {

private EditText mTo;
private EditText mBody;
private Button mSend;
private OkHttpClient mClient = new OkHttpClient();
private Context mContext;

In the body of the onCreate method, initialise the widgets so we get references to our UI.


@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   mTo = (EditText) findViewById(R.id.txtNumber);
   mBody = (EditText) findViewById(R.id.txtMessage);
   mSend = (Button) findViewById(R.id.btnSend);
   mContext = getApplicationContext();
}

Now create a new method in this class called post. This method will return the response from the HTTP request we will make to our backend application.

Call post(String url, Callback callback) throws IOException{
    RequestBody formBody = new FormBody.Builder()
            .add("To", mTo.getText().toString())
            .add("Body", mBody.getText().toString())
            .build();
    Request request = new Request.Builder()
            .url(url)
            .post(formBody)
            .build();
Call response = mClient.newCall(request);
    response.enqueue(callback);
    return response;
}

The code above will take a URL and a callback as arguments, and will build a new multipart form body for us, which we will then pass along to a new asynchronous request.

The last thing we need to do now is hook this up to our button, so when we press it, the post method gets called and the SMS message is sent.

Back on the onCreate method, paste the following code just under the send variable.


mSend.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        try {
            post(mContext.getString("YOUR_NGROK_URL/sms"), new  Callback(){

                @Override
                public void onFailure(Call call, IOException e) {
                    e.printStackTrace();
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mTo.setText("");
                            mBody.setText("");
                            Toast.makeText(getApplicationContext(),"SMS Sent!",Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
});

 

We use the onClick listener method on the button to to know when to make a request to our post method.

Run the application again, and when the Send button is clicked we pass on the URL we want to post our data to, and upon getting a callback we either show an error message, or display a Toast on the screen saying the SMS message was sent.

ahoy-from-twilio.gif

Your app is now safe

There’s a warm feeling about knowing that we were able to squash at least one vulnerability from our app by just following a few simple steps. It is also great to now have a backend we can use with other other platforms as we can just perform direct HTTP requests to it.

Using a pattern like this, we could also add phone number lookups to an application or generate phone calls. If you want to use IP Messaging, Video or Client, you’ll want a server to generate access tokens for those services too. Get your backend right and you’re ready to do anything with Twilio in your application.

I would love to know more about the apps you’re building. Hit me up on Twitter @marcos_placona or by email on marcos@twilio.com to tell me more about them.