Android - прослушивание входящих SMS-сообщений


155

Я пытаюсь создать приложение для мониторинга входящих SMS-сообщений и запустить программу с помощью входящих SMS-сообщений, также она должна читать содержимое SMS-сообщений.

Процедура:

  • SMS отправлено на устройство Android
  • самоисполняемое приложение
  • Прочитайте информацию SMS

1
Я знаю, как создать приложение для отправки SMS, но здесь мне нужно создать приложение SMS, которое получает информацию из SMS и сохраняет ее в базе данных SQLite ..... Как мне разработать такое приложение
iShader

@ iShader Я надеюсь, что вы успешно создали приложение, просто хотели узнать, как вам удалось синхронизировать сообщения с ч / б устройством и сервером
Джон Х

Ответы:


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

Примечание. В файле манифеста добавьте BroadcastReceiver-

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

Добавьте это разрешение:

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

2
Можете ли вы объяснить мне, почему вы используете дополнительный приемник?
WindRider

2
@ VineetShukla не могли бы вы объяснить, что такое pdus ??
TheGraduateGuy

11
используйте Intents.SMS_RECEIVED_ACTION вместо жестко закодированного.
Ахмад Каяли

6
Приведенный выше комментарий не является правильным. Любое приложение все еще может получать SMS_RECEIVEDтрансляцию в 4.4+, и теперь, когда эта трансляция не может быть прервана, это более точно, чем в предыдущих версиях.
Майк М.

3
@RuchirBaronia Multipart сообщения. Одно SMS-сообщение имеет ограничение на количество символов (оно зависит от используемого набора символов, но общие ограничения составляют 70, 140, 160 символов). Если сообщение превышает этот предел, оно может быть разделено на несколько сообщений, частей. Этот массив представляет собой массив частей, которые необходимо объединить, чтобы получить полное сообщение. Ваш получатель будет получать только одно полное сообщение за раз; это просто может быть в нескольких частях.
Майк М.

65

Обратите внимание, что на некоторых устройствах ваш код не будет работать без android: priority = "1000" в фильтре намерений:

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

И вот некоторые оптимизации:

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

Примечание :
Значение должно быть целым числом, например, «100». Более высокие числа имеют более высокий приоритет. Значение по умолчанию - 0. Значение должно быть больше -1000 и меньше 1000.

Вот ссылка.


30
Этот ответ может быть более элегантным, но требует API 19. Просто к сведению других.
baekacaek

10
В соответствии с этим , android:priorityне может быть выше 1000(или меньше -1000).
вытянул

2
Не работает на Xiaomi Redmi Note 3 Pro с Android 5.1. Все предоставляют это решение, но, похоже, оно мне не подходит.
Sermilion

Где в файле манифеста вставлена ​​разметка <receive ...?
Джон Уорд

3
@Sermilion Вы должны вручную разрешить разрешение на чтение SMS в диспетчере приложений мобильного телефона.
Санджай Кушва

6

@ Майк М. и я нашли проблему с принятым ответом (см. Наши комментарии):

По сути, нет смысла проходить цикл for, если мы не объединяем составное сообщение каждый раз:

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

Обратите внимание, что мы просто устанавливаем msgBodyстроковое значение соответствующей части сообщения, независимо от того, по какому индексу мы находимся, что делает бесполезным весь цикл прохождения по различным частям SMS-сообщения, поскольку оно будет просто установлено на последнее значение индекса Вместо этого мы должны использовать +=, или как указано Майк StringBuilder:

В общем, вот как выглядит мой код получения 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
}

Просто поместите этот ответ на тот случай, если у кого-то возникнет такая же путаница.


4

Это то, что я использовал!

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

        }
    }
}

2

В случае, если вы хотите обработать намерение в открытой деятельности, вы можете использовать PendintIntent (выполните шаги ниже):

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

манифест:

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

            }

разрешения:

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

Администраторы Google для Google Play Store считают разрешение RECEIVE_SMS (в упомянутом учебнике) опасным. В результате приложение, содержащее разрешение, будет отклонено. Затем разработчик должен отправить форму администраторам Google Play для утверждения. Другие разработчики отметили, что процесс ужасен, поскольку обратная связь занимает недели и получает откровенные отказы без объяснений или общих отзывов. Любые идеи о том, как избежать?
AJW

2

Если кто-то ссылается на то, как сделать ту же функцию (чтение OTP с помощью полученных SMS) на Xamarin Android, как я:

  1. Добавьте этот код в свой файл 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. Затем создайте свой класс BroadcastReveiver в своем проекте 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. Зарегистрируйте этот класс BroadcastReceiver в своем классе MainActivity на 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"));
    
            }
    }

Получил ошибку компилятора, говоря, что "android.permission.BROADCAST_SMS" предоставляется только системным приложениям.
совершиландройдер

2

Спасибо @Vineet Shukla (принятый ответ) и @Ruchir Baronia (нашел проблему в принятом ответе), ниже приведен Kotlin версия:

Добавить разрешение:

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

Зарегистрируйте BroadcastReceiver в 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>

Добавьте реализацию для 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) {
                }
            }
        }
    }
}

Иногда событие запускается дважды, поэтому я добавляю mLastTimeReceived = System.currentTimeMillis()


1

Реализация трансляции на Котлин:

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

Примечание. В файле манифеста добавьте BroadcastReceiver-

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

Добавьте это разрешение:

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

1

Принятый ответ правильный и работает на более старых версиях Android, где ОС Android запрашивает разрешения при установке приложения, однако в более новых версиях Android это не работает сразу, потому что более новая ОС Android запрашивает разрешения во время выполнения, когда приложение требует эту функцию , Поэтому, чтобы получать SMS на более новых версиях Android, используя технику, упомянутую в принятом ответе, программист должен также реализовать код, который будет проверять и запрашивать разрешения у пользователя во время выполнения. В этом случае функциональность / код проверки прав могут быть реализованы в onCreate () первой активности приложения. Просто скопируйте и вставьте следующие два метода в ваше первое действие и вызовите метод checkForSmsReceivePermissions () в конце 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");
        }
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.