Accessing user messages is a sensitive matter that requires a strong commitment to privacy and ethical responsibility.

Why?

As engineers, we must anticipate the questions users may have when we request sensitive permissions, such as:

  • Why do you need to read my messages?
  • How will my data be used?
  • Will you access any sensitive information?

It’s our responsibility to address these concerns before requesting access. Before prompting users for sensitive permissions, we should critically evaluate whether the access is truly necessary. Consider:

  • Are we building a messaging application where reading messages is essential for core functionality?
  • Is there a legitimate, user-focused reason to access this data?

If, after thoughtful consideration, we determine that reading user messages is required, the next step is to approach this need transparently and securely, ensuring we respect user privacy at every stage.

How?

1

Update Manifest

Add the telephony feature and required permissions to your app’s AndroidManifest.xml:

<uses-feature
    android:name="android.hardware.telephony"
    android:required="true"
    tools:ignore="UnnecessaryRequiredFeature" />

<uses-permission android:name="android.permission.READ_SMS" />
Only set required="true" if your app cannot function without this feature

If you omit the telephony feature, you’ll encounter errors like:
Permission exists without corresponding hardware ... tag.

2

Request Permission

There are multiple ways to request permissions in Android. Below are two common approaches.

This method uses Android’s built-in APIs:

// create permission reference
val permission = android.Manifest.permission.READ_SMS

// get activity & context
val activity: ComponentActivity = getActivity()
val context: Context = getContext()

// check if permission is granted
val isGranted = context.checkSelfPermission(permission) == android.content.pm.PackageManager.PERMISSION_GRANTED
if (isGranted) return

// if it is not granted, check if it can show permission dialog
val canRequest = ActivityCompat.shouldShowRequestPermissionRationale(this, permission)
if (canRequest.not()) {
    // navigate the user to settings to enable permission manually
    return
}

// create launcher and launch permission
with(activity){
    val launcher: ActivityResultLauncher<String> =
        registerForActivityResult(
            ActivityResultContracts.RequestPermission()
        ) { result ->
            // check if result is true and continue
            // if result is not true, the permission was not granted
        }
    launcher.launch(permission)
}
The launcher can only be created from the context of a ComponentActivity
3

Read Messages

Once permissions are granted, you can access SMS data:

// get context
val context: Context = getContext()

// get content resolver from context
val contentResolver: ContentResolver = context.contentResolver

// get cursor for
val cursor =
    context.contentResolver.query("content://sms/inbox".toUri(), null, null, null, null)

// use the cursor to read contents of the column
cursor?.use {
    while(it.moveToNext()){
        val date = it.getLongOrNull("date") // get message date
        val body = it.getStringOrNull("body") // get message body
    }
}

Conclusion

Reading user messages should never be taken lightly. Use this capability only when it is indispensable to your app’s functionality. Always follow these guidelines:

  • Clearly explain to users why the permission is needed.
  • Handle sensitive data responsibly, respecting user privacy at all times.
  • Stay updated on Android’s permission policies to maintain compliance.

With great power comes great responsibility. Build trust with your users by prioritizing their privacy and offering a transparent experience.

Now, you’re equipped to responsibly read device messages in your Android application.