Programmatic messaging services like Twilio, Vonage and others alike can be useful for various use cases, especially when your infrastructure is deployed in the cloud. But when you have an on-premises infrastructure, you can avoid Twilio or Vonage. Sure, delegating customer messages delivering to external programmatic messaging services can help you deal with less components in your infrastructure and this can greatly increase productivity. But if you ever found yourself in an internet constrained environment, that makes external API calls a nightmare, then you should keep on reading this article. Besides that, if you don’t want to burn money in the cloud using those programmatic messaging services ie. you want to minimize costs, then you should also keep reading this.
A few weeks ago, I did a a Youtube video explaining how you can use Android Debug Bridge (ADB) to achieve wireless debugging of Android apps. And since then, I kept exploring the possibilities we have with the ADB tool. What I found out is that, ADB is a very powerful tool that you can use for various tasks including UI automation on your android phone. So I thought: wouldn’t it be great to automate SMS sending from android phone ? I then decided to investigate and I found out some useful informations that I gathered to produce the essence of this post.
We’re going to proceed using the following steps:
👉️ Get an Android phone + a prepaid SIM card
👉️ Install Android Debug Bridge aka ADB on our operating system
👉️ Connect our android phone and our operating system
👉️ Get a shell session into our android phone.
👉️ Test the command to send SMS with our android phone from our OS
👉️ Write a shell script to automate the message sending commands
👉️ Write a nodejs program that can call the command
👉️ Wrapup.
So let’s get in !!!
I think the first step is the easiest one. If you are already an Android user, you have probably a smartphone at your disposal along with a prepaid sim card. Other than that, we can assume that first step is completed without even being started 😄️.
For the second step (installing ADB on our operating system), I will assume we are all on a Linux system, specifically an ubuntu based distro and then ADB installation is simple as doing the following:
sudo apt install android-tools-adb If you have Android Studio installed on your system and the platforms tools already downloaded, then you already have ADB. What you can do is make it globally available on the system by doing a symlink on the Linux system like below:
sudo ln -s ~/Android/Sdk/platform-tools/adb /usr/local/bin On Windows, you’d have to add it to the PATH. I’m not going to cover the windows steps of doing that, since I’m a Linux fanboy 🫠️ and I don’t dig a lot in Windows related stuffs. But if you do some google search on it, you will undoubtfully find a way for it.
The next step is to connect our android phone to our computer. We can do this by using a USB cable. After the phone is connected to the computer, we need to make sure that USB debugging mode is enabled. If you don’t know what USB debugging mode is or how to enable it, you can have more info here
Once our phone is connected to the computer and USB debugging enabled, we can get a shell session into the Android phone by issuing the command below:
adb shell After that command is issued, you get a shell session into your Android phone and then your shell prompt should now look something like below:
TECNO-KG7h:/ $ Thus, the third step is done. Now we can experiment sending the SMS from our Android phone through the adb shell session. We have two options: Entering the shell session before sending instructions to the Android phone via Unix-y commands or even better we can directly send the commands to the android phone by doing things like :
adb shell The latter is the one I prefer and I’ll use that for the rest of this tutorial. Adb shell commands are numerous, you can refer to this gist to find the one you want. Sending SMS on Android means launching the Android phone SMS app, writing the SMS content and sending it to a given phone number. All that process can be done through the shell command below:
adb shell am start -a android.intent.action.SENDTO -d sms:$phone_number --es sms_body $body You need to replace $phone_number with the actual phone number you want the SMS to be sent to and $body with your actual message to that phone number. Great ! But we still have a problem 🤔️. When we run that command, we notice that it launches the Android Message app, writes the message and set the phone number to which the message should be sent to but we don’t have a way to hit the send button on the screen. This is where things get a little bit interesting. When I was experimenting with this, I tried using the KEYCODE_ENTER (66) to do it but it didn’t worked out because the SEND button is built by the application developers and that button responds to tap events. So the ultimate solution is to locate that button on the UI, get its coordinates and send them as arguments to ‘adb shell input tap’ command.
To get the coordinates of the button, we’re going to use UI automator, a tool whose primary purpose is to automate Android applications UI testing, look at it as the Playwright of Android applications. We’ll then resort to perl to do some strings manipulations in order to extract the button’s coordinates. The process is summarized in the commands below:
adb pull $(adb shell uiautomator dump | grep -oP '[^ ]+.xml') /tmp/view.xml
sms_send_btn_coords=$(perl -ne 'printf "%d %d\n", ($1+$3)/2, ($2+$4)/2 if /content-desc="Send SMS"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/' /tmp/view.xml The content-desc in the second command may be different on your Android phone, in which case you can view the xml generated by the first command in your browser and locate the SEND button then get its content-desc or resource id.
Once those commands completed, you should have the SEND button coordinates in the $sms_send_btn_coords shell variable. We are now going to use that variable to automate the action of pressing the SEND button which will actually send the SMS. Pressing on the SEND button can now be done through the command below:
adb shell input tap $sms_send_btn_coords That command presses the SEND button which sends the SMS 🤩️ ! Interesting, isn’t it !! If you’ve come this far, then you are one of the lucky people who ever manipulate their Android phones from their computers to automate actions and even send SMS. Congrats 🙂️ !! Since we are a little bit perfectionnist, we can go even further and close the application after the SMS has been sent. We can do so with the following command: adb shell am force-stop com.google.android.apps.messaging Now that we’ve done all this, we notice one more problem. We cannot be doing all this manually everytime we want to send a single SMS. We can do a better job by putting all those commands in one shell script, give execution right to that script and then call it. Below is an example for that script we can use to make our life easier
#!/usr/bin/env bash
while getopts n:b: flag
do
case "${flag}" in
n) phone_number=${OPTARG};;
b) body=${OPTARG};;
esac
done
# this wakes up the android phone screen 😉️
adb shell input keyevent KEYCODE_WAKEUP
adb shell am start -a android.intent.action.SENDTO -d sms:$phone_number --es sms_body $body
adb pull $(adb shell uiautomator dump | grep -oP '[^ ]+.xml') /tmp/view.xml
sms_send_btn_coords=$(perl -ne 'printf "%d %d\n", ($1+$3)/2, ($2+$4)/2 if /content-desc="Send SMS"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/' /tmp/view.xml)
adb shell input tap $sms_send_btn_coords
adb shell am force-stop com.google.android.apps.messaging
# this powers off the android phone screen 😉️
adb shell input keyevent KEYCODE_POWER
Assuming we put the shell script in a file called sendsms we can give it execution permissions and move it to a global place by doing the following:
sudo chmod +x sendsms && mv sendsms /usr/local/bin With that command, you’ve made the sendsms command callable and callable from anywhere on your system. You can then call the command like this:
sendsms -n "12345678" -b "mymessage" Now let’s write a simple NodeJS program that calls our command. The essence of this part is the concept of child_process in NodeJS which allows us to call the underlying OS commands or programs from the NodeJS runtime. The example is like below.
const { spawn } = require("child_process");
let regex = /[.*+?^${}()|[\]\\' ]/g;
let message = "Message to a random number"
// necessary because the special characters in the message string must be escaped
var escapedMsgStr = message.replace(regex, "\\$&");
const command = 'sendsms';
const args = [
'-n', '123456789',
'-b', escapedStr
];
const sendsms = spawn(command, args);
sendsms.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
sendsms.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
Assuming we call the NodeJS script file sendsms.js, to execute it, we will do the following:
node sendsms.js This wraps up all we need to do to programmatically send SMS using our Android phone. Overall we can say that adb is a powerful tool that can help us automate common actions on our android phone. We can use it for various purposes. Sending SMS programmatically being one of them. Sending programmatic SMS using our Android phone like that can help use minimize costs. Especially if our system is deployed on-premises, we can use that method and forget about services like Twilio or Vonage. We can also use that process as a way of testing SMS sending from our programs or API during development time.
NB: All the above steps assume your system is on-premises (eg: internal servers of your organisation) and possibly made available on the internet via tunneling technologies like ngrok or cloudflared. They also assume that the android phone used does not have screen lock enabled (You would need some extra logic if screen lock is enabled, that extra logic is left for you to genuinely implement 😁️)
Thank you for reading along. We hope you enjoyed this post. All the code samples can be found in this github repository Don’t forget to subscribe to our Youtube channel. See you soon and until then.. Peace, fellow geeks !