Crack the OTP Code: Easy SMS Reading Techniques for Android Developers
In today’s mobile-first world, seamless user experiences are crucial, especially regarding authentication. One common method is using One-Time Passwords (OTPs) sent via SMS. But how can you read these SMS messages and extract the OTP effortlessly in your Android app?
In this blog, we’ll dive into various methods to read SMS in Android using Kotlin and show you how to implement them step-by-step.
Why Read SMS for OTP Verification?
Reading SMS messages allows you to streamline the login process by automatically retrieving the OTP sent to users. This enhances user experience by reducing manual input errors and speeding up authentication.
Method 1: Using the SMS Retriever API
The SMS Retriever API is a powerful tool provided by Google that allows you to receive SMS messages without needing SMS permissions. Let’s get started!
Step 1: Set Up Your Project
First, ensure you have the necessary dependencies in your build.gradle
file:
dependencies {
implementation 'com.google.android.gms:play-services-auth-api-phone:19.2.0'
}
Step 2: Start the SMS Retriever
In your activity, initialize the SMS Retriever:
import com.google.android.gms.auth.api.phone.SmsRetriever
fun startSmsRetriever() {
val client = SmsRetriever.getClient(this)
val task = client.startSmsRetriever()
task.addOnSuccessListener {
// SMS Retriever started successfully
}.addOnFailureListener {
// Failed to start SMS Retriever
}
}
Step 3: Create a BroadcastReceiver
Create a BroadcastReceiver
to handle incoming SMS messages. This receiver will extract the OTP for you.
class SmsBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (SmsRetriever.SMS_RETRIEVED_ACTION == intent?.action) {
val extras: Bundle? = intent.extras
val status = extras?.get(SmsRetriever.EXTRA_STATUS) as? Int
if (status == SmsRetriever.RESULT_STATUS_OK) {
val message = extras.get(SmsRetriever.EXTRA_SMS_MESSAGE) as? String
val otp = extractOtp(message)
Log.d("SmsReceiver", "OTP received: $otp")
// Use the OTP as needed
}
}
}
private fun extractOtp(message: String?): String? {
return message?.split(" ")?.find { it.length == 6 && it.all { char -> char.isDigit() } }
}
}
Step 4: Register the BroadcastReceiver
In your activity, register the BroadcastReceiver
to listen for SMS:
override fun onStart() {
super.onStart()
val intentFilter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
registerReceiver(smsBroadcastReceiver, intentFilter)
}
override fun onStop() {
super.onStop()
unregisterReceiver(smsBroadcastReceiver)
}
Step 5: Send a Properly Formatted SMS
Make sure the SMS you send is formatted correctly, like this:
Your verification code is: 123456
This will ensure that the SMS Retriever can extract the OTP easily.
Method 2: Using a BroadcastReceiver to Read Incoming SMS
This method provides a straightforward way to read SMS messages with the necessary permissions while giving you control over incoming messages as they are received.
Step 1: Declare Permissions
Add the following permission to your AndroidManifest.xml
:
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
Step 2: Create a BroadcastReceiver
Create a BroadcastReceiver that listens for incoming SMS messages:
class SmsReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == "android.provider.Telephony.SMS_RECEIVED") {
val bundle: Bundle? = intent.extras
val msgs = bundle?.get("pdus") as Array<*>
for (msg in msgs) {
val smsMessage = SmsMessage.createFromPdu(msg as ByteArray)
val sender = smsMessage.displayOriginatingAddress
val messageBody = smsMessage.messageBody
Log.d("SmsReceiver", "Sender: $sender, Message: $messageBody")
// Here you can extract and process the OTP if needed
}
}
}
}
Step 3: Register the Receiver in Manifest
Register your BroadcastReceiver
in the AndroidManifest.xml
:
<receiver android:name=".SmsReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
Step 4: Handle Permissions
Ensure you handle permissions, especially on devices running Android 6.0 (API level 23) and above:
private val REQUEST_CODE_RECEIVE_SMS = 101
private fun checkSmsPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS),
REQUEST_CODE_RECEIVE_SMS)
} else {
// Permission granted; ready to receive SMS
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_RECEIVE_SMS && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted; ready to receive SMS
}
}
Method 3: Using Content Resolver
If you need more control and want to read SMS messages directly, you can use a ContentResolver
. This method requires READ_SMS
permission.
Step 1: Declare Permissions
Add the following permission to your AndroidManifest.xml
:
<uses-permission android:name="android.permission.READ_SMS"/>
Step 2: Request Permission
In your activity, check and request the permission:
private val REQUEST_CODE_READ_SMS = 100
private fun checkSmsPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.READ_SMS),
REQUEST_CODE_READ_SMS)
} else {
readSms()
}
}
Step 3: Read SMS
Now you can read SMS messages using the ContentResolver
:
private fun readSms() {
val cursor = contentResolver.query(
Uri.parse("content://sms/inbox"),
null, null, null, null
)
cursor?.use {
while (it.moveToNext()) {
val id = it.getString(it.getColumnIndexOrThrow("_id"))
val address = it.getString(it.getColumnIndexOrThrow("address"))
val body = it.getString(it.getColumnIndexOrThrow("body"))
Log.d("SMS", "ID: $id, Address: $address, Body: $body")
}
}
}
Step 4: Handle Permissions Result
Make sure to handle the permission result:
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_READ_SMS && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
readSms()
}
}
Now you have three powerful methods to read SMS and verify OTPs in your Android application using Kotlin!
Key Differences
Below, we’ll explain the key differences between these approaches to help you decide which best suits your application’s needs.
Permissions Required:
SMS Retriever API: No permissions are needed to read SMS.
BroadcastReceiver: Requires both
RECEIVE_SMS
andREAD_SMS
permissions.Content Resolver: Requires
READ_SMS
permission.
Privacy:
SMS Retriever API: Enhances user privacy; does not access all SMS.
BroadcastReceiver: Less privacy; intercepts all incoming SMS.
Content Resolver: Less privacy; accesses all SMS messages.
Implementation Complexity:
SMS Retriever API: Simpler for OTP retrieval.
BroadcastReceiver: Moderate complexity; requires handling permissions and managing received messages.
Content Resolver: More complex; needs permission handling and SMS management.
Real-Time Retrieval:
SMS Retriever API: Retrieves SMS only when specifically formatted for the API.
BroadcastReceiver: Captures SMS messages in real-time as they arrive.
Content Resolver: Can read all SMS messages from the inbox anytime.
SMS Format:
SMS Retriever API: Requires specific formatting (e.g., with a hash).
BroadcastReceiver: No specific format is required; captures all incoming messages.
Content Resolver: No specific format is required; you can read any SMS message.
Use Case:
SMS Retriever API: Best for OTP verification and temporary messages.
BroadcastReceiver: Ideal for applications requiring immediate access to incoming SMS.
Content Resolver: Suitable for apps needing broader SMS access (e.g., messaging apps).
User Experience:
SMS Retriever API: Seamless; no interruptions for permission prompts.
BroadcastReceiver: This may interrupt user experience due to permission requests.
Content Resolver: This may interrupt user experience due to permission requests.
Message Filtering:
SMS Retriever API: Automatically filters messages based on predefined criteria.
BroadcastReceiver: Requires manual processing of intercepted messages.
Content Resolver: Requires manual filtering and processing of messages.
Summary
SMS Retriever API is ideal for OTP retrieval and authentication scenarios, providing a simple and privacy-friendly approach.
BroadcastReceiver captures incoming SMS messages in real-time but also requires permissions and processes all messages, which may only sometimes be desirable for user privacy.
Content Resolver offers broader access to SMS but requires more setup and user permissions, making it suitable for apps that need comprehensive SMS management.
Each method has strengths and weaknesses, so the best choice depends on your app’s specific needs and the user experience you want to provide!
References:
SMS Retriever API — https://developers.google.com/identity/sms-retriever/overview
Broadcast Receiver — https://developer.android.com/develop/background-work/background-tasks/broadcasts
Content Provider — https://developer.android.com/guide/topics/providers/content-providers
Subscribe to my newsletter
Read articles from Pragnesh Ghoda directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Pragnesh Ghoda
Pragnesh Ghoda
Hi 👋, I'm Pragnesh Ghoda I am an Android enthusiasts and passionate about all things related to this dynamic and exciting operating system. I constantly try keeping up with the latest developments in the world of Android, and sharing my knowledge with readers. 🌱 I’m currently learning Flutter 📝 I regularly write articles on https://androidacademic.blogspot.com/ 📖 Check my latest article on about Github Actions on https://androidacademic.blogspot.com/2023/03/implement-cicd-using-github-actions.html