How can I read SMS messages from the device programmatically in Android?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Reading SMS messages programmatically on Android involves querying the system SMS content provider using the ContentResolver API. This requires proper runtime permissions and careful handling of the cursor-based query results. Since SMS data is considered sensitive, Google Play enforces strict policies on which apps may declare SMS permissions, so understanding both the technical implementation and the policy requirements is essential.
Declaring Permissions in the Manifest
Before your app can read SMS messages, you must declare the READ_SMS permission in your AndroidManifest.xml. Without this declaration, any attempt to query the SMS content provider will fail silently or throw a security exception.
The RECEIVE_SMS permission is only needed if you also want to listen for incoming messages in real time via a BroadcastReceiver.
Requesting Runtime Permissions
Starting with Android 6.0 (API 23), dangerous permissions like READ_SMS must be requested at runtime in addition to being declared in the manifest. The user must explicitly grant the permission before your code can access SMS data.
Reading SMS Messages with ContentResolver in Kotlin
The SMS content provider exposes messages through the URI content://sms/inbox for received messages and content://sms/sent for sent messages. You query it using ContentResolver and iterate through the returned Cursor.
The cursor?.use {} block ensures the cursor is closed automatically when processing is complete, preventing resource leaks.
Reading SMS Messages in Java
The same logic in Java uses a try-finally pattern to ensure the cursor is properly closed.
Querying Specific SMS Types
The SMS content provider supports several URI paths for different message categories. You can also use selection filters to narrow results.
Google Play Policy Restrictions
Google Play restricts the use of SMS and call log permissions to apps that require them as core functionality. Since January 2019, only apps set as the default SMS handler or those with an approved use case (such as call forwarding or backup apps) may use these permissions in production apps distributed through the Play Store.
If your app reads SMS solely for verification codes, use the SMS Retriever API or SMS User Consent API instead. These APIs do not require the READ_SMS permission and are compliant with Play Store policies.
Common Pitfalls
- Forgetting runtime permission checks: Declaring
READ_SMSin the manifest is not enough on Android 6.0 and above. You must also request it at runtime or the query returns null. - Not closing the cursor: Failing to close the
Cursorafter querying causes memory leaks. Always usecursor.use {}in Kotlin or a try-finally block in Java. - Hardcoding SMS URI strings: Using
"content://sms/inbox"directly is fragile. While it works on most devices, theTelephony.Smscontract class provides constants that are more reliable across manufacturers. - Ignoring Play Store policy: Publishing an app with
READ_SMSpermission without a qualifying use case will result in your app being rejected or removed from Google Play. - Assuming consistent column names: Some device manufacturers modify the SMS content provider schema. Always use
getColumnIndexOrThrow()rather than hardcoded column indices to handle variations gracefully.
Summary
- Declare
READ_SMSinAndroidManifest.xmland request it at runtime for Android 6.0 and above. - Query the SMS content provider using
ContentResolver.query()with URIs likecontent://sms/inboxorcontent://sms/sent. - Always close the cursor after use to prevent memory leaks.
- Use selection parameters to filter by sender, read status, or date range.
- For verification codes, prefer the SMS Retriever API to avoid needing the
READ_SMSpermission entirely. - Review Google Play policies before publishing, as SMS permissions are restricted to apps with core SMS functionality.

