androidinterview.com

Android System Design Interview Questions

What is the SMS Retriever API in Android?

Tier: Less commonDifficulty: Easy

The SMS Retriever API lets your app automatically read a one time verification code from an incoming SMS, without asking the user for the READ_SMS or RECEIVE_SMS permission.

That permission free part is the whole point. Reading arbitrary SMS content is a serious privacy grant, Play Store policy restricts it heavily, and users are rightly wary of an app asking for it just to autofill an OTP. The SMS Retriever API sidesteps that entirely, using an app specific hash instead of a broad permission.

The flow looks like this.

  • Your app starts listening by calling SmsRetriever.getClient(context).startSmsRetriever().
  • The verification SMS your backend sends must include an 11 character hash at the end of the message, computed from your app's package name and signing certificate.
  • When a message carrying that exact hash arrives, Google Play services delivers its content to your app through a broadcast, matched only by the hash, without your app ever being granted general SMS read access.
  • Your app extracts the code from the message text, typically with a regex, and fills it in automatically.

The tradeoffs worth naming. It only works for messages formatted with that specific hash suffix, so it's opt in on the backend, you can't retrofit it onto an existing SMS format without changing what you send. And the listener times out after five minutes, so it's meant for the "waiting on this screen right now" case, not a general purpose SMS reader. Given those constraints, it's the correct default for OTP autofill, the alternative of requesting RECEIVE_SMS for the same job is a permission most users would rightly decline.

How I'd build this on Android

OTP verification componentsCompose route and ViewModel to Verification repository, submit code. Verification repository to Backend and SMS delivery, server verification.submit codeserver verificationCompose route and ViewModelManual entry, autofill and challenge stateVerification repositoryRequest code and verify challengeBackend and SMS deliveryExpiry, attempt limits and app hash
OTP verification components
Each arrow shows a call from one component to another.

I'd treat autofill as a convenience. The backend still checks that the code belongs to the current challenge, has not expired and is within the attempt limit. The app hash helps deliver the message to the right app. It is not a secret that proves who sent the message.

interface VerificationRepository {
    suspend fun requestCode(phone: String): String // Challenge ID.
    suspend fun verify(challengeId: String, code: String): Boolean
}

// The route starts SMS Retriever through a lifecycle-owned platform adapter.
// Both autofill and manual entry call the same ViewModel action.
fun submitCode(code: String) = viewModelScope.launch {
    _state.value = Verifying
    _state.value = if (repository.verify(challengeId, code)) Verified else InvalidCode
}

This example assumes the ViewModel has a saved challenge ID and verification state. Network failure is different from an incorrect code, and repeated taps must not submit twice. An adapter owns the receiver for its required lifetime, checks the broadcast action and uses the documented sender permission for SMS Retriever. It unregisters when finished. Manual input remains available if Play services is missing or the five minute listening window expires.

Compose collects state with lifecycle awareness and sends edits and Submit through callbacks. OTP text never belongs in logs, analytics or a history table. A DAO is unnecessary unless recovery requires saving challenge metadata that contains no secret.

I'd inject the repository and SMS adapter so tests can control them.

I'd check these cases.

  • Resend replacing the old challenge.
  • Timeout.
  • Manual entry.
  • The wrong signing certificate hash in a release build.
  • A late SMS from the previous challenge.

See SMS Retriever flow.

Read more Request SMS verification in an Android app (opens in a new tab)