Подтвердить что ты не робот

Использование SMS для проверки номера телефона устройства

Я пытаюсь проверить номер телефона Android-устройства, отправив SMS-сообщение самому себе и автоматически проверяя, получено ли SMS. Как я могу это сделать?

4b9b3361

Ответ 1

Для начала потребуется два разрешения; один для отправки SMS-сообщений и один для их получения. В вашем AndroidManifest.xml должно быть следующее: между тегами <manifest>, но вне тегов <application>.

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

Это опасные разрешения, поэтому вам нужно будет обрабатывать их соответственно, если ваше приложение должно работать на Marshmallow (уровень API 23) или выше и имеет targetSdkVersion из 23+. Информацию о том, как запрашивать эти разрешения во время выполнения, можно найти на этой странице разработчика.


Классы Java, которые вам понадобятся, находятся в пакете android.telephony; в частности android.telephony.SmsManager и android.telephony.SmsMessage. Убедитесь, что у вас есть правильные классы, импортированные для обоих.

Чтобы отправить исходящее SMS, вы будете использовать метод SmsManager sendTextMessage(), который имеет следующую подпись:

sendTextMessage(String destinationAddress, String scAddress, String text,
                PendingIntent sentIntent, PendingIntent deliveryIntent)

В этом вызове метода требуются только два аргумента - destinationAddress и text; первый из которых является номером телефона, второй - содержимым сообщения. null может быть передано для остальных. Например:

String number = "1234567890";
String message = "Verification message.";
SmsManager sm = SmsManager.getDefault();
sm.sendTextMessage(number, null, message, null, null);

Важно, чтобы текст сообщения был относительно коротким, так как sendTextMessage() обычно терпит неудачу, если длина текста превышает ограничение символа для одного сообщения.


Чтобы получать и читать входящее сообщение, вам необходимо зарегистрировать BroadcastReceiver с IntentFilter для действия "android.provider.Telephony.SMS_RECEIVED". Этот приемник может быть зарегистрирован либо статически в манифесте, либо динамически на Context во время выполнения.

  • Статическая регистрация класса Receiver в манифесте позволит вашему приложению получать входящее сообщение, даже если ваше приложение должно быть убито до получения. Однако, возможно, потребуется немного дополнительной работы, чтобы получить результаты там, где вы хотите. Между тегами <application>:

    <receiver
        android:name=".SmsReceiver"
        android:enabled="false">
        <intent-filter>
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
    </receiver>
    

    Метод PackageManager#setComponentEnabledSetting() может использоваться для включения и отключения этого <receiver> по мере необходимости.

  • Динамическая регистрация экземпляра получателя на Context может быть немного проще в управлении, по коду, поскольку класс Receiver может быть сделан внутренним классом на том, что компонент регистрирует его, и, следовательно, имеет прямой доступ к эти компоненты. Однако этот подход может быть не таким надежным, как статическая регистрация, поскольку несколько разных вещей могут помешать Получателю получать трансляцию; например, ваш процесс приложения убит, пользователь переходит от регистрации Activity и т.д.

    SmsReceiver receiver = new SmsReceiver();
    IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
    registerReceiver(receiver, filter);
    

    Помните о необходимости отменить регистрацию приемника, если это необходимо.


В методе Receiver onReceive() фактическое сообщение приходит как массив массивов byte, прикрепленных к Intent в качестве дополнительного. Детали декодирования варьируются в зависимости от версии Android, но результатом здесь является один объект SmsMessage, в котором будут указаны номер телефона и сообщение, которое вы после.

class SmsReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        SmsMessage msg;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            SmsMessage[] msgs = Telephony.Sms.Intents.getMessagesFromIntent(intent);
            msg = msgs[0];
        } else {
            Object pdus[] = (Object[]) intent.getExtras().get("pdus");
            msg = SmsMessage.createFromPdu((byte[]) pdus[0]);
        }

        String number = msg.getOriginatingAddress();
        String message = msg.getMessageBody();
        ...
    }
}

На этом этапе вы просто сравниваете number с тем, который был передан вызову sendTextMessage(). Целесообразно использовать для этого PhoneNumberUtils.compare(), поскольку номер, полученный в приемнике, может быть в другом формате, чем тот, который был адресован.


Примечания:

  • Пример, продемонстрированный здесь, заключается в использовании одного сообщения с одной частью, поэтому текст сообщения должен быть ограничен относительно короткой длиной. Если вы хотите отправить более длинное сообщение, по какой-либо причине вместо него можно использовать метод sendMultipartTextMessage(). Сначала вам нужно разделить текст, используя SmsManager#divideMessage(), и передать полученный ArrayList этому методу вместо сообщения String. Чтобы собрать полное сообщение в приемнике, вам нужно будет декодировать каждый byte[] в SmsMessage и объединить тела сообщений.

  • Поскольку KitKat (API-уровень 19), если ваше приложение не является приложением по умолчанию для обмена сообщениями, сообщения, используемые здесь, будут сохраняться в поставщике SMS с помощью системы и приложения по умолчанию и поэтому будут доступны для любое другое приложение, которое использует Поставщик. Не так много вы можете с этим поделать, но если вы действительно хотите этого избежать, эту же методику можно использовать с данными SMS, которые не запускают приложение по умолчанию, и не будут сохранены в Провайдере.

    Для этого используется метод sendDataMessage(), которому потребуется дополнительный аргумент short для номера (произвольного) порта, и сообщение передается как byte[], а не String. Действие для фильтрации по умолчанию - "android.intent.action.DATA_SMS_RECEIVED", и для фильтра потребуется схема данных и полномочия (узел и порт). В манифесте это будет выглядеть так:

    <intent-filter>
        <action android:name="android.intent.action.DATA_SMS_RECEIVED" /> 
        <data
            android:scheme="sms"
            android:host="localhost"
            android:port="1234" /> 
    </intent-filter>
    

    и в классе IntentFilter есть соответствующие методы, чтобы динамически их устанавливать.

    Декодирование SmsMessage одно и то же, но сообщение byte[] извлекается с помощью getUserData(), а не getMessageBody().

  • До появления KitKat приложения отвечали за запись собственных исходящих сообщений, поэтому вы можете просто не делать этого в этих версиях, если вам не нужна его запись.

    Входящие сообщения могут быть перехвачены, а их трансляции прерваны до того, как основное приложение обмена сообщениями сможет их получить и записать. Для этого приоритет фильтра устанавливается на максимум, а abortBroadcast() вызывается в приемнике. В статической опции атрибут android:priority="999" добавляется в открывающий тег <intent-filter>. Динамически метод IntentFilter#setPriority() может сделать то же самое.

    Это совсем не надёжно, так как для другого приложения всегда возможно иметь более высокий приоритет, чем ваш.

  • Я отказался обеспечить приемник с помощью разрешения вещателя в этих примерах, отчасти для простоты и ясности, а отчасти потому, что природа вещи не оставила бы вас открытой для каких-либо подделок, которые могли бы сделать вред. Однако, если вы хотите включить это, вам просто нужно добавить атрибут android:permission="android.permission.BROADCAST_SMS" к открытому тегу <receiver> для статического параметра. Для динамического использования используйте четырехпараметрическую перегрузку метода registerReceiver(), передав это разрешение String в качестве третьего аргумента и null в качестве четвертого.

Ответ 2

Sinch SDK позволяет вам проверить номер телефона пользователя с помощью технологии вызова вспышки. Он полагается на обычную телефонную сеть, поэтому, если номер телефона пользователя действителен, вы получаете пропущенный вызов.

private void startVerification(String phoneNumber) {
 Config config = SinchVerification.config().applicationKey("your_app_key").context(getApplicationContext()).build(); 
 VerificationListener listener = new MyVerificationListener(); 
 verification = SinchVerification.createFlashCallVerification(config, phoneNumber, listener); 
 verification.initiate(); 
}

Для получения дополнительной информации вы можете посмотреть этот учебник:

https://www.sinch.com/tutorials/android-flash-call-verification/