Android - Écoutez les SMS entrants

155

J'essaie de créer une application pour surveiller les messages SMS entrants et de lancer un programme via SMS entrants, il doit également lire le contenu du SMS.

Flux de travail:

  • SMS envoyé à un appareil Android
  • Application auto-exécutable
  • Lire les informations SMS
iShader
la source
1
Je sais créer une application pour envoyer le SMS, mais ici, je dois créer une application SMS qui récupère les informations du SMS et l'enregistre dans la base de données SQLite ..... Comment puis-je développer une telle application
iShader
@iShader J'espère que vous avez réussi à créer l'application, je voulais juste savoir comment avez-vous réussi à synchroniser les msgs entre l'appareil et le serveur
John x

Réponses:

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());
                }
            }
        }
    }
}

Remarque: dans votre fichier manifeste, ajoutez le BroadcastReceiver-

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

Ajoutez cette autorisation:

<uses-permission android:name="android.permission.RECEIVE_SMS" />
Vineet Shukla
la source
2
Pouvez-vous m'expliquer pourquoi vous utilisez un récepteur secondaire?
WindRider
2
@VineetShukla pouvez-vous s'il vous plaît expliquer ce qu'est pdus ??
TheGraduateGuy
11
utilisez Intents.SMS_RECEIVED_ACTION au lieu de celui codé en dur.
Ahmad Kayyali
6
Le commentaire ci-dessus n'est pas correct. Toute application peut toujours obtenir la SMS_RECEIVEDdiffusion en 4.4+, et, maintenant que cette diffusion ne peut pas être annulée, elle est plus sûre que dans les versions précédentes.
Mike M.
3
@RuchirBaronia Messages en plusieurs parties. Un seul SMS a une limite de caractères (cela varie en fonction du jeu de caractères que vous utilisez, mais les limites courantes sont de 70, 140, 160 caractères). Si un message dépasse cette limite, il peut être divisé en plusieurs messages, parties. Ce tableau est le tableau de parties que vous devez concaténer pour obtenir le message complet. Votre récepteur ne recevra qu'un seul message complet à la fois; cela pourrait être en plusieurs parties.
Mike M.
65

Notez que sur certains appareils, votre code ne fonctionnera pas sans Android: priority = "1000" dans le filtre d'intention:

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

Et voici quelques optimisations:

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();
            }
        }
    }
}

Remarque :
la valeur doit être un entier, tel que «100». Les nombres plus élevés ont une priorité plus élevée. La valeur par défaut est 0. La valeur doit être supérieure à -1000 et inférieure à 1000.

Voici un lien.

stefan.nsk
la source
30
Cette réponse peut être plus élégante, mais nécessite l'API 19. Juste un FYI pour les autres.
baekacaek
10
Selon cela , android:priorityne peut pas être supérieur à 1000(ou inférieur à -1000).
craned le
2
Cela ne fonctionne pas sur Xiaomi Redmi Note 3 Pro avec Android 5.1. Tout le monde propose cette solution, mais cela ne semble pas fonctionner pour moi.
Sermilion
Où le balisage <receiver ... est-il inséré dans le fichier manifeste?
John Ward
3
@Sermilion Vous devez autoriser manuellement l'autorisation de lire les SMS dans le gestionnaire d'applications du mobile.
Sanjay Kushwah
6

@Mike M. et moi avons trouvé un problème avec la réponse acceptée (voir nos commentaires):

Fondamentalement, il ne sert à rien de passer par la boucle for si nous ne concaténons pas le message en plusieurs parties à chaque fois:

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();
}

Notez que nous venons de définir msgBodyla valeur de chaîne de la partie respective du message, quel que soit l'index sur lequel nous nous trouvons, ce qui rend le point entier de la boucle à travers les différentes parties du message SMS inutile, car il sera simplement défini sur le très dernière valeur d'index. Au lieu de cela, nous devrions utiliser +=, ou comme Mike l'a noté StringBuilder:

Dans l'ensemble, voici à quoi ressemble mon code de réception 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
}

Il suffit de mettre cette réponse au cas où quelqu'un d'autre aurait la même confusion.

Ruchir Baronia
la source
4

C'est ce que j'ai utilisé!

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);

        }
    }
}
Dégrader Ghosh
la source
2

Si vous souhaitez gérer l'intention sur l'activité ouverte, vous pouvez utiliser PendintIntent (Suivez les étapes ci-dessous):

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) {
        }
    }
} 

manifeste:

<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"));

            }

autorisations:

<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
la source
Les administrateurs Google du Google Play Store considèrent que l'autorisation RECEIVE_SMS (dans le didacticiel que vous mentionnez) est dangereuse. Par conséquent, une application qui contient l'autorisation sera rejetée. Ensuite, le développeur doit soumettre un formulaire aux administrateurs de Google Play pour approbation. D'autres développeurs ont mentionné que le processus était horrible, les commentaires prenant des semaines et recevant des refus catégoriques sans explications ni commentaires génériques. Des idées sur la façon d'éviter?
AJW
2

Si quelqu'un se réfère à la même fonctionnalité (lecture d'OTP à l'aide de SMS reçus) sur Xamarin Android comme moi:

  1. Ajoutez ce code à votre fichier 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. Créez ensuite votre classe BroadcastReveiver dans votre projet 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. Enregistrez cette classe BroadcastReceiver dans votre classe MainActivity sur 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
la source
Vous avez une erreur de compilation indiquant que "android.permission.BROADCAST_SMS" n'est accordé qu'aux applications système.
committedandroider
2

Merci à @Vineet Shukla (la réponse acceptée) et @Ruchir Baronia (a trouvé le problème dans la réponse acceptée), voici le Kotlin version:

Ajouter une autorisation:

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

Enregistrez BroadcastReceiver dans 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>

Ajouter une implémentation pour 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) {
                }
            }
        }
    }
}

Parfois, l'événement se déclenche deux fois alors j'ajoute mLastTimeReceived = System.currentTimeMillis()

Menteur
la source
1

diffusion de l'implémentation sur 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
    }
}

Remarque: dans votre fichier manifeste, ajoutez le BroadcastReceiver-

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

Ajoutez cette autorisation:

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

La réponse acceptée est correcte et fonctionne sur les anciennes versions d'Android où Android OS demande des autorisations lors de l'installation de l'application.Cependant, sur les versions plus récentes d'Android, cela ne fonctionne pas tout de suite car le système d'exploitation Android plus récent demande des autorisations pendant l'exécution lorsque l'application nécessite cette fonctionnalité . Par conséquent, pour recevoir des SMS sur les nouvelles versions d'Android en utilisant la technique mentionnée dans la réponse acceptée, le programmeur doit également implémenter un code qui vérifiera et demandera les autorisations de l'utilisateur pendant l'exécution. Dans ce cas, la fonctionnalité / le code de vérification des autorisations peut être implémenté dans onCreate () de la première activité de l'application. Copiez et collez simplement en suivant deux méthodes dans votre première activité et appelez la méthode checkForSmsReceivePermissions () à la fin 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
la source