Android Receive SMS And Verify OTP Automatically Tutorial

This tutorial helps you to read otp from SMS automatically in our Android application(For Any API version). We will use a SMS Retriever API to receive the messages and use EditText to show message. Nowadays, Applications usually use SMS texts for authentication purpose, like OTP (One-Time-Passwords), where a message is sent by the service-provider and it is automatically read by the application, verifying it. This flow helps user to save a lot of app switching from application to messenger app and then entering the authentication text to app again.

SMS Retriever API

Google changed in policy. From Jan 19th, 2019 google removed all app from play store with permission CALL_LOG and READ_SMS. Google introduced SMS Retriever API to automatically fetch a verification code sent through SMS within the app.

OTP Message Format

Google introduced a new format for OTP message. Message should be in the below format:
  • SMS should start with # tag
  • Content should not be greater than 140 Character. Content like: Your OTP is: 156367
  • SMS should end with application hash code. Hash Code received from LOG_CAT generated by the SmsHashCode helper.
Message Format should be like – # Your otp code is : 123456 L9js2MM0qvM.

Create a new project

We are going to create a new android project. Go to File ⇒ New ⇒ New Projects in Android studio.

Add gradle dependency

To integrate SMS Retriever API, add the below dependency in the app level build.gradle file.
implementation 'com.google.android.gms:play-services-auth:17.0.0'
implementation 'com.google.android.gms:play-services-auth-api-phone:17.0.0'

Generate Hashcode

Google restricted the sms reading in the app. Google introduce a format for otp message. Hashcode must be include at the end of message. Hashcode should be made with Package Name. Create a class named is SmsHashCodeHelper and paste the below code. This is simplest way to get the Hashcode. We can get it in the CMD. Once we generated Hashcode then it never use. We can delete it. Call getAppHashCode() methods in onCreate() fo MainActivity. Note: – It supports minSdkVersion: 19.
Don't Miss
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.util.Base64;
import android.util.Log;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;

public class SmsHashCodeHelper extends ContextWrapper {
    private static final String TAG = "SmsHashCodeHelper";
    private static final String HASH_TYPE = "SHA-256";
    public static final int NUM_HASHED_BYTES = 9;
    public static final int NUM_BASE64_CHAR = 11;

    public SmsHashCodeHelper(Context context) {
        super(context);
    }

    private static String hash(String packageName, String signature) {
        String appInfo = packageName + " " + signature;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(HASH_TYPE);
            messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
            byte[] hashSignature = messageDigest.digest();
            // truncated into NUM_HASHED_BYTES
            hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
            // encode into Base64
            String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
            base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);
            Log.d(TAG, String.format("Package: %s -- hash: %s", packageName, base64Hash));
            return base64Hash;
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "hash:NoSuchAlgorithm", e);
        }
        return null;
    }

    /**
     * Get the application hashcode for the current package
     */
    public ArrayList getAppHashCode() {
        ArrayList appCodes = new ArrayList<>();
        try {
            // Get all package signatures for the current package
            String packageName = getPackageName();
            PackageManager packageManager = getPackageManager();
            Signature[] signatures = packageManager.getPackageInfo(packageName,
                    PackageManager.GET_SIGNATURES).signatures;
            // For each signature create a compatible hash
            for (Signature signature : signatures) {
                String hash = hash(packageName, signature.toCharsString());
                if (hash != null) {
                    appCodes.add(String.format("%s", hash));
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Unable to find package to obtain hash.", e);
        }
        return appCodes;
    }
}
HashCodeGenerate-min

Create a Interface

We need to create interface listener to send the OTP to activity. Create a new class Java file named as GetOtpInterface. Add below code in this class.
public interface GetOtpInterface {
    void onOtpReceived(String otp);
    void onOtpTimeout();
}
interfaceOTP

Create MySMSBroadCastReceiver Java file

To create BroadCastReceiver, we need to create a java file named as MySMSBroadCastReceiver.java. To read sms, We will extends BroadcastReceiver class in MySMSBroadCastReceiver class. In this class, we will create a onReceive() method with context and intent as parameter. This intent contains SmsRetriever.EXTRA_SMS_MESSAGE which is a protocol for transfer of SMS messages. We obtain an array of these messages that were sent to to our receiver by system. Here we are getting 6 digit otp code in the message so, we are getting only digit. We can change it according to requirement.
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MySMSBroadCastReceiver extends BroadcastReceiver {
    private static final String TAG = "SmsBroadcastReceiver";
    GetOtpInterface getOtpInterface = null;
    public void setOnOtpListeners(GetOtpInterface getOtpInterface) {
        Log.d(TAG, "onReceive: InterFace ");
        this.getOtpInterface = getOtpInterface;
    }
    @Override public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "onReceive: ");
        if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
            Bundle extras = intent.getExtras();
            Status mStatus = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
            Log.d(TAG, "onReceive: status code " + mStatus.getStatusCode());
            switch (mStatus.getStatusCode()) {
                case CommonStatusCodes.SUCCESS:
                    // Get SMS message contents'
                    String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
                    if (getOtpInterface != null) {
                        Log.d(TAG, "onReceive: Success "+message);
                        // Here we are using 6 digit Otp code.
                        Pattern pattern = Pattern.compile("(|^)\\d{6}");
                        Matcher matcher = pattern.matcher(message);
                        if (matcher.find()) {
                            Toast.makeText(context, "Full Message Received " + message, Toast.LENGTH_LONG).show();
                            getOtpInterface.onOtpReceived(matcher.group(0));
                        }

                    }
                    break;
                case CommonStatusCodes.TIMEOUT:
                    // Waiting for SMS timed out (5 minutes)
                    Log.d(TAG, "onReceive: failure");
                    if (getOtpInterface != null) {
                        getOtpInterface.onOtpTimeout();
                    }
                    break;
            }
        }
    }
}

Add MySMSBroadCastReceiver in AndroidMenifest

To read SMS, we need to add Broadcast Receiver in the application section of AndroidMenifest.xml file.
<receiver android:name=".MySMSBroadCastReceiver" android:permission="com.google.android.gms.auth.api.phone.permission.SEND" android:exported="true">
            <intent-filter>
                <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED"/>
            </intent-filter>
</receiver>

Add TextView & EditText in Layout

We will display message in Acitivity class when user get the OTP SMS, so we need to add EditText in the acivity_main.xml file.

<xml version="1.0" encoding="utf-8"?&>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dp"
        android:text="Otp is"
        android:textSize="100px"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <EditText
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="150dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

</androidx.constraintlayout.widget.ConstraintLayout>

Obtain the user’s phone number

We are using hint picker to obtain the user’s phone number.
public void getHintPhoneNumber() {
        HintRequest hintRequest =
                new HintRequest.Builder()
                        .setPhoneNumberIdentifierSupported(true)
                        .build();
        PendingIntent mIntent = Auth.CredentialsApi.getHintPickerIntent(mGoogleApiClient, hintRequest);
        try {
            startIntentSenderForResult(mIntent.getIntentSender(), RESOLVE_HINT, null, 0, 0, 0);
        } catch (IntentSender.SendIntentException e) {
            e.printStackTrace();
        }
}

SMS Listner

Call the SMS listner after the requesting otp from server API.
    public void smsListener() {
        SmsRetrieverClient mClient = SmsRetriever.getClient(this);
        Task mTask = mClient.startSmsRetriever();
        mTask.addOnSuccessListener(new OnSuccessListener() {
            @Override public void onSuccess(Void aVoid) {

                Toast.makeText(MainActivity.this, "SMS Retriever Started", Toast.LENGTH_LONG).show();
            }
        });
        mTask.addOnFailureListener(new OnFailureListener() {
            @Override public void onFailure(@NonNull Exception e) {
                Toast.makeText(MainActivity.this, "Error", Toast.LENGTH_LONG).show();
            }
        });
    }

Implement GoogleApiClient & GetOtpInterface

We need to to implement GoogleApiClient & GetOtpInterface listener to get SMS message in the Activity.

package in.studytutorial.otpsmsreader;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.Toast;

import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;

public class MainActivity extends AppCompatActivity implements  GoogleApiClient.ConnectionCallbacks,
        GetOtpInterface, GoogleApiClient.OnConnectionFailedListener{

    public static final int REQUEST_ID_MULTIPLE_PERMISSIONS = 1;
    GoogleApiClient mGoogleApiClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //set google api client for hint request
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .enableAutoManage(this, this)
                .addApi(Auth.CREDENTIALS_API)
                .build();

    }

    @Override public void onConnected(@Nullable Bundle bundle) {
    }
    @Override public void onConnectionSuspended(int i) {
    }
    @Override public void onOtpReceived(String otp) {
        Toast.makeText(this, "Otp Received " + otp, Toast.LENGTH_LONG).show();
    }
    @Override public void onOtpTimeout() {
        Toast.makeText(this, "Time out, please resend", Toast.LENGTH_LONG).show();
    }
    @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
    }
}

Final Acitivity Class


import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.app.PendingIntent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.credentials.HintRequest;
import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.auth.api.phone.SmsRetrieverClient;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;

public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks,
        GetOtpInterface, GoogleApiClient.OnConnectionFailedListener {
    GoogleApiClient mGoogleApiClient;
    MySMSBroadCastReceiver mySMSBroadCastReceiver;
    private int RESOLVE_HINT = 2;
    TextView inputOtp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        inputOtp = findViewById(R.id.textView);
        // init broadcast receiver
        mySMSBroadCastReceiver = new MySMSBroadCastReceiver();

        //set google api client for hint request
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .enableAutoManage(this, this)
                .addApi(Auth.CREDENTIALS_API)
                .build();
        mySMSBroadCastReceiver.setOnOtpListeners(this);
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(SmsRetriever.SMS_RETRIEVED_ACTION);
        getApplicationContext().registerReceiver(mySMSBroadCastReceiver, intentFilter);
        // get mobile number from phone
        getHintPhoneNumber();
        //start SMS listner
        smsListener();
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
    }

    @Override
    public void onConnectionSuspended(int i) {
    }

    @Override
    public void onOtpReceived(String otp) {
        Toast.makeText(this, "Otp Received " + otp, Toast.LENGTH_LONG).show();
        inputOtp.setText(otp);
    }

    @Override
    public void onOtpTimeout() {
        Toast.makeText(this, "Time out, please resend", Toast.LENGTH_LONG).show();
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
    }

    public void smsListener() {
        SmsRetrieverClient mClient = SmsRetriever.getClient(this);
        Task<Void> mTask = mClient.startSmsRetriever();
        mTask.addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {

                Toast.makeText(MainActivity.this, "SMS Retriever Started", Toast.LENGTH_LONG).show();
            }
        });
        mTask.addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Toast.makeText(MainActivity.this, "Error", Toast.LENGTH_LONG).show();
            }
        });
    }

    /**
     * @desc This function is using hint picker to show user's phone number
     */
    public void getHintPhoneNumber() {
        HintRequest hintRequest =
                new HintRequest.Builder()
                        .setPhoneNumberIdentifierSupported(true)
                        .build();
        PendingIntent mIntent = Auth.CredentialsApi.getHintPickerIntent(mGoogleApiClient, hintRequest);
        try {
            startIntentSenderForResult(mIntent.getIntentSender(), RESOLVE_HINT, null, 0, 0, 0);
        } catch (IntentSender.SendIntentException e) {
            e.printStackTrace();
        }
    }
}
Android_SMS_OTP_Read_Automatically

24 COMMENTS

  1. Sir, Code is working fine when use with main activity but it does not work when use with other activities. I am using a different activity for otp entering. Please help

  2. Hi Abhay,

    This tutorial is very clean explained … but how i test it. on Emulator?

    Could you please guide us also?

    Thanks

    • Go to settings in emulator and then in Phone tab you can add anything you require under message and under phone number

  3. The purpose of this whole tutorial.. is extremely weird. I need OTP bypass, whenever i sign up in some site, and they ask phone verification.. can i receive their sms in this messed up android studio OTP thingy???

  4. Your broadcast receiver is initiating twice. One by LocalBroadcastManager and other when receiving SMS. This is because you have declared receiver in Manifest file. For LocalBroadcastManager you don’t require to declare in Manifest file. I was looking for solution to receive SMS without Manifest and solely to use LocalBroadcastManager, but your solution is just adding up unwanted burden on system.

  5. Like!! I blog frequently and I really thank you for your content. The article has truly peaked my interest.

  6. Excellent goods from you, man. I’ve understand your stuff previous
    to and you’re just too magnificent. I really like what you’ve acquired here, really like what
    you are saying and the way in which you say it.

    You make it enjoyable and you still take care of to keep it wise.
    I cant wait to read much more from you. This is actually a
    tremendous site.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

564FansLike

Recent Posts

Concept of Session in Laravel Tutorial

Sessions are used to store details about the user throughout the requests. Laravel supplies various drivers like file, cookie, apc, array, Memcached, Redis, and database to handle session data. By default, file driver is used as a result of it's light-weight....

Laravel Url Generation Tutorial

Our web application revolves around routes and URLs. After all, they're what direct our users to our pages. At the end of the day, serving pages is what any web application should do. Our users may...

Concept of Laravel Views Tutorial

In MVC framework, the letter "V" stands for Views. It separates the application logic and presentation logic. Views are saved in resources/views listing. Generally, the view contains the HTML which might be served by the application.

Related Articles

Concept of Session in Laravel Tutorial

Sessions are used to store details about the user throughout the requests. Laravel supplies various drivers like file, cookie, apc, array, Memcached, Redis, and database to handle session data. By default, file driver is used as a result of it's light-weight....

Laravel Url Generation Tutorial

Our web application revolves around routes and URLs. After all, they're what direct our users to our pages. At the end of the day, serving pages is what any web application should do. Our users may...

Concept of Laravel Views Tutorial

In MVC framework, the letter "V" stands for Views. It separates the application logic and presentation logic. Views are saved in resources/views listing. Generally, the view contains the HTML which might be served by the application.
WP2Social Auto Publish Powered By : XYZScripts.com