Android - Ouça as mensagens SMS recebidas

155

Estou tentando criar um aplicativo para monitorar as mensagens SMS recebidas e iniciar um programa via SMS recebida, também deve ler o conteúdo do SMS.

Fluxo de trabalho:

  • SMS enviado para o dispositivo Android
  • Aplicativo auto-executável
  • Leia as informações do SMS
iShader
fonte
1
Eu sei que para criar um aplicativo para enviar o SMS, mas aqui eu preciso para criar um aplicativo SMS que recebe as informações do SMS e guardá-lo para banco de dados SQLite ..... Como posso desenvolver tais App
iShader
@iShader eu espero que você tem com sucesso na criação do aplicativo, só queria saber como você conseguiu sincronizar as msgs b / w o dispositivo eo servidor
John x

Respostas:

265
public class SmsListener extends BroadcastReceiver{

    private SharedPreferences preferences;

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub

        if(intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")){
            Bundle bundle = intent.getExtras();           //---get the SMS message passed in---
            SmsMessage[] msgs = null;
            String msg_from;
            if (bundle != null){
                //---retrieve the SMS message received---
                try{
                    Object[] pdus = (Object[]) bundle.get("pdus");
                    msgs = new SmsMessage[pdus.length];
                    for(int i=0; i<msgs.length; i++){
                        msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
                        msg_from = msgs[i].getOriginatingAddress();
                        String msgBody = msgs[i].getMessageBody();
                    }
                }catch(Exception e){
//                            Log.d("Exception caught",e.getMessage());
                }
            }
        }
    }
}

Nota: No seu arquivo de manifesto, adicione o BroadcastReceiver-

<receiver android:name=".listener.SmsListener">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

Adicione esta permissão:

<uses-permission android:name="android.permission.RECEIVE_SMS" />
Vineet Shukla
fonte
2
Você pode me explicar por que usa um receptor secundário?
WindRider
2
@VineetShukla você pode explicar o que é pdus ??
TheGraduateGuy
11
use Intents.SMS_RECEIVED_ACTION em vez do codificado.
Ahmad Kayyali
6
O comentário acima não está correto. Qualquer aplicativo ainda pode receber a SMS_RECEIVEDtransmissão em 4.4+ e, agora que essa transmissão não pode ser abortada, é mais certo do que nas versões anteriores.
Mike M.
3
@RuchirBaronia Mensagens de várias partes. Uma única mensagem SMS tem um limite de caracteres (varia de acordo com o conjunto de caracteres que você está usando, mas os limites comuns são 70, 140, 160 caracteres). Se uma mensagem exceder esse limite, ela poderá ser dividida em várias mensagens, partes. Essa matriz é a matriz de partes que você precisa concatenar para obter a mensagem completa. Seu receptor receberá apenas uma mensagem completa por vez; só pode estar em várias partes.
Mike M.
65

Observe que em alguns dispositivos seu código não funciona sem o android: priority = "1000" no filtro de intenção:

<receiver android:name=".listener.SmsListener">
    <intent-filter android:priority="1000">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

E aqui estão algumas otimizações:

public class SmsListener extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
            for (SmsMessage smsMessage : Telephony.Sms.Intents.getMessagesFromIntent(intent)) {
                String messageBody = smsMessage.getMessageBody();
            }
        }
    }
}

Nota :
O valor deve ser um número inteiro, como "100". Números mais altos têm uma prioridade mais alta. O valor padrão é 0. O valor deve ser maior que -1000 e menor que 1000.

Aqui está um link.

stefan.nsk
fonte
30
Essa resposta pode ser mais elegante, mas requer a API 19. Apenas um FYI para outras pessoas.
precisa saber é
10
De acordo com isso , android:prioritynão pode ser maior que 1000(ou menor que -1000).
craned
2
Ele não funciona no Xiaomi Redmi Note 3 Pro com Android 5.1. Todo mundo está fornecendo essa solução, mas parece não funcionar para mim.
Sermilion
Onde está a marcação <receiver ... inserida no arquivo de manifesto?
John Ward
3
@Sermilion Você deve permitir manualmente a permissão para ler SMS no gerenciador de aplicativos do celular.
Sanjay Kushwah
6

@Mike M. e eu encontrei um problema com a resposta aceita (veja nossos comentários):

Basicamente, não faz sentido passar pelo loop for se não estivermos concatenando a mensagem de várias partes a cada vez:

for (int i = 0; i < msgs.length; i++) {
    msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
    msg_from = msgs[i].getOriginatingAddress();
    String msgBody = msgs[i].getMessageBody();
}

Observe que acabamos de definir msgBodyo valor da string da parte respectiva da mensagem, independentemente do índice em que estamos, o que torna inútil todo o ponto de percorrer as diferentes partes da mensagem SMS, uma vez que ela será ajustada último valor do índice. Em vez disso, devemos usar +=, ou como Mike observou, StringBuilder:

Em suma, eis a aparência do meu código de recebimento de SMS:

if (myBundle != null) {
    Object[] pdus = (Object[]) myBundle.get("pdus"); // pdus is key for SMS in bundle

    //Object [] pdus now contains array of bytes
    messages = new SmsMessage[pdus.length];
    for (int i = 0; i < messages.length; i++) {
         messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]); //Returns one message, in array because multipart message due to sms max char
         Message += messages[i].getMessageBody(); // Using +=, because need to add multipart from before also
    }

    contactNumber = messages[0].getOriginatingAddress(); //This could also be inside the loop, but there is no need
}

Basta colocar essa resposta no caso de alguém ter a mesma confusão.

Ruchir Baronia
fonte
4

Isto é o que eu usei!

public class SMSListener extends BroadcastReceiver {

    // Get the object of SmsManager
    final SmsManager sms = SmsManager.getDefault();
String mobile,body;

    public void onReceive(Context context, Intent intent) {

        // Retrieves a map of extended data from the intent.
        final Bundle bundle = intent.getExtras();

        try {

            if (bundle != null) {

                final Object[] pdusObj = (Object[]) bundle.get("pdus");

                for (int i = 0; i < pdusObj.length; i++) {

                    SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
                    String phoneNumber = currentMessage.getDisplayOriginatingAddress();

                    String senderNum = phoneNumber;
                    String message = currentMessage.getDisplayMessageBody();
                     mobile=senderNum.replaceAll("\\s","");
                     body=message.replaceAll("\\s","+");


                    Log.i("SmsReceiver", "senderNum: "+ senderNum + "; message: " + body);


                    // Show Alert
                    int duration = Toast.LENGTH_LONG;
                    Toast toast = Toast.makeText(context,
                            "senderNum: "+ mobile+ ", message: " + message, duration);
                    toast.show();

                } // end for loop
            } // bundle is null

        } catch (Exception e) {
            Log.e("SmsReceiver", "Exception smsReceiver" +e);

        }
    }
}
Debasish Ghosh
fonte
2

Caso deseje lidar com a intenção em atividade aberta, você pode usar PendintIntent (etapas completas abaixo):

public class SMSReciver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        final Bundle bundle = intent.getExtras();
        try {
            if (bundle != null) {
                final Object[] pdusObj = (Object[]) bundle.get("pdus");
                for (int i = 0; i < pdusObj.length; i++) {
                    SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
                    String phoneNumber = currentMessage.getDisplayOriginatingAddress();
                    String senderNum = phoneNumber;
                    String message = currentMessage.getDisplayMessageBody();
                    try {
                        if (senderNum.contains("MOB_NUMBER")) {
                            Toast.makeText(context,"",Toast.LENGTH_SHORT).show();

                            Intent intentCall = new Intent(context, MainActivity.class);
                            intentCall.putExtra("message", currentMessage.getMessageBody());

                            PendingIntent pendingIntent= PendingIntent.getActivity(context, 0, intentCall, PendingIntent.FLAG_UPDATE_CURRENT);
                            pendingIntent.send();
                        }
                    } catch (Exception e) {
                    }
                }
            }
        } catch (Exception e) {
        }
    }
} 

manifesto:

<activity android:name=".MainActivity"
            android:launchMode="singleTask"/>
<receiver android:name=".SMSReciver">
            <intent-filter android:priority="1000">
                <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
            </intent-filter>
        </receiver>

onNewIntent:

 @Override
         protected void onNewIntent(Intent intent) {
                super.onNewIntent(intent);
                Toast.makeText(this, "onNewIntent", Toast.LENGTH_SHORT).show();

                onSMSReceived(intent.getStringExtra("message"));

            }

permissões:

<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
AskQ
fonte
Os administradores do Google para a Google Play Store consideram a permissão RECEIVE_SMS (no tutorial que você mencionou) perigosa. Como resultado, um aplicativo que contém a permissão será rejeitado. Em seguida, o desenvolvedor deve enviar um formulário aos administradores do Google Play para aprovação. Outros desenvolvedores mencionaram que o processo é péssimo, com o retorno de semanas e o recebimento de rejeições diretas, sem explicações ou feedback genérico. Alguma idéia de como evitar?
AJW 15/07
2

Se alguém se referir a como fazer o mesmo recurso (lendo OTP usando SMS recebido) no Xamarin Android como eu:

  1. Adicione este código ao seu arquivo AndroidManifest.xml:

    <receiver android:name=".listener.BroadcastReveiverOTP">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
    </receiver>
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.BROADCAST_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />
    
  2. Em seguida, crie sua classe BroadcastReveiver em seu projeto Android.

    [BroadcastReceiver(Enabled = true)] [IntentFilter(new[] { "android.provider.Telephony.SMS_RECEIVED" }, Priority = (int)IntentFilterPriority.HighPriority)] 
    public class BroadcastReveiverOTP : BroadcastReceiver {
            public static readonly string INTENT_ACTION = "android.provider.Telephony.SMS_RECEIVED";
    
            protected string message, address = string.Empty;
    
            public override void OnReceive(Context context, Intent intent)
            {
                if (intent.HasExtra("pdus"))
                {
                    var smsArray = (Java.Lang.Object[])intent.Extras.Get("pdus");
                    foreach (var item in smsArray)
                    {
                        var sms = SmsMessage.CreateFromPdu((byte[])item);
                        address = sms.OriginatingAddress;
                        if (address.Equals("NotifyDEMO"))
                        {
                            message = sms.MessageBody;
                            string[] pin = message.Split(' ');
                            if (!string.IsNullOrWhiteSpace(pin[0]))
                            { 
                                    // NOTE : Here I'm passing received OTP to Portable Project using MessagingCenter. So I can display the OTP in the relevant entry field.
                                    MessagingCenter.Send<object, string>(this,MessengerKeys.OnBroadcastReceived, pin[0]);
                            }
                            }
                    }
                }
            }
    }
    
  3. Registre esta classe BroadcastReceiver na sua classe MainActivity no Android Project:

    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity {
    
            // Initialize your class
            private BroadcastReveiverOTP _receiver = new BroadcastReveiverOTP ();
    
            protected override void OnCreate(Bundle bundle) { 
                    base.OnCreate(bundle);
    
                    global::Xamarin.Forms.Forms.Init(this, bundle);
                    LoadApplication(new App());
    
                    // Register your receiver :  RegisterReceiver(_receiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
    
            }
    }
    
Pabodha Wimalasuriya
fonte
Ocorreu um erro no compilador dizendo que "android.permission.BROADCAST_SMS" é concedido apenas para aplicativos do sistema.
committedandroider
2

Obrigado a @Vineet Shukla (a resposta aceita) e @Ruchir Baronia (encontrou o problema na resposta aceita), abaixo está a Kotlinversão:

Adicionar permissão:

<uses-permission android:name="android.permission.RECEIVE_SMS" />

Registrar o BroadcastReceiver no AndroidManifest:

<receiver
    android:name=".receiver.SmsReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter android:priority="2332412">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

Adicione implementação para BroadcastReceiver:

class SmsReceiver : BroadcastReceiver() {
    private var mLastTimeReceived = System.currentTimeMillis()

    override fun onReceive(p0: Context?, intent: Intent?) {
        val currentTimeMillis = System.currentTimeMillis()
        if (currentTimeMillis - mLastTimeReceived > 200) {
            mLastTimeReceived = currentTimeMillis

            val pdus: Array<*>
            val msgs: Array<SmsMessage?>
            var msgFrom: String?
            var msgText: String?
            val strBuilder = StringBuilder()
            intent?.extras?.let {
                try {
                    pdus = it.get("pdus") as Array<*>
                    msgs = arrayOfNulls(pdus.size)
                    for (i in msgs.indices) {
                        msgs[i] = SmsMessage.createFromPdu(pdus[i] as ByteArray)
                        strBuilder.append(msgs[i]?.messageBody)
                    }

                    msgText = strBuilder.toString()
                    msgFrom = msgs[0]?.originatingAddress

                    if (!msgFrom.isNullOrBlank() && !msgText.isNullOrBlank()) {
                        //
                        // Do some thing here
                        //
                    }
                } catch (e: Exception) {
                }
            }
        }
    }
}

Algum evento dispara duas vezes, então eu adiciono mLastTimeReceived = System.currentTimeMillis()

Mentiroso
fonte
1

implementação de transmissão no Kotlin:

 private class SmsListener : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        Log.d(TAG, "SMS Received!")

        val txt = getTextFromSms(intent?.extras)
        Log.d(TAG, "message=" + txt)
    }

    private fun getTextFromSms(extras: Bundle?): String {
        val pdus = extras?.get("pdus") as Array<*>
        val format = extras.getString("format")
        var txt = ""
        for (pdu in pdus) {
            val smsmsg = getSmsMsg(pdu as ByteArray?, format)
            val submsg = smsmsg?.displayMessageBody
            submsg?.let { txt = "$txt$it" }
        }
        return txt
    }

    private fun getSmsMsg(pdu: ByteArray?, format: String?): SmsMessage? {
        return when {
            SDK_INT >= Build.VERSION_CODES.M -> SmsMessage.createFromPdu(pdu, format)
            else -> SmsMessage.createFromPdu(pdu)
        }
    }

    companion object {
        private val TAG = SmsListener::class.java.simpleName
    }
}

Nota: No seu arquivo de manifesto, adicione o BroadcastReceiver-

<receiver android:name=".listener.SmsListener">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

Adicione esta permissão:

<uses-permission android:name="android.permission.RECEIVE_SMS" />
Serg Burlaka
fonte
1

A resposta aceita está correta e funciona em versões mais antigas do Android, nas quais o sistema operacional Android solicita permissões na instalação do aplicativo. No entanto, nas versões mais recentes, o Android não funciona imediatamente porque o sistema operacional Android mais recente solicita permissões durante o tempo de execução, quando o aplicativo exige esse recurso. . Portanto, para receber SMS em versões mais recentes do Android usando a técnica mencionada no programador de respostas aceito, também é necessário implementar o código que verificará e solicitará permissões do usuário durante o tempo de execução. Nesse caso, a verificação da funcionalidade / código pode ser implementada em onCreate () da primeira atividade do aplicativo. Apenas copie e cole seguindo dois métodos em sua primeira atividade e chame o método checkForSmsReceivePermissions () no final de onCreate ().

    void checkForSmsReceivePermissions(){
    // Check if App already has permissions for receiving SMS
    if(ContextCompat.checkSelfPermission(getBaseContext(), "android.permission.RECEIVE_SMS") == PackageManager.PERMISSION_GRANTED) {
        // App has permissions to listen incoming SMS messages
        Log.d("adnan", "checkForSmsReceivePermissions: Allowed");
    } else {
        // App don't have permissions to listen incoming SMS messages
        Log.d("adnan", "checkForSmsReceivePermissions: Denied");

        // Request permissions from user 
        ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.RECEIVE_SMS}, 43391);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(requestCode == 43391){
        if(grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
            Log.d("adnan", "Sms Receive Permissions granted");
        } else {
            Log.d("adnan", "Sms Receive Permissions denied");
        }
    }
}
Adnan Ahmed
fonte