Я пытаюсь проверить номер телефона Android-устройства, отправив SMS-сообщение самому себе и автоматически проверяя, получено ли SMS. Как я могу это сделать?
Использование SMS для проверки номера телефона устройства
Ответ 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/