web-dev-qa-db-ja.com

SMSを使用してデバイスの電話番号を確認する

デバイスにAndroidを送信してSMSを受信したかどうかを自動的に確認することで、SMSデバイスの電話番号を確認しようとしています。これどうやってするの?

26
Sohan Alam

まず、これには2つの権限が必要です。 1つはSMSメッセージを送信し、もう1つはメッセージを受信します。以下は、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を送信するには、SmsManagersendTextMessage()メソッドを使用します。このメソッドには次の署名があります。

_sendTextMessage(String destinationAddress, String scAddress, String text,
                PendingIntent sentIntent, PendingIntent deliveryIntent)
_

このメソッド呼び出しで必要な引数は、destinationAddresstextの2つだけです。 1つ目は電話番号、2つ目はメッセージの内容です。残りの場合、nullを渡すことができます。例えば:

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

sendTextMessage()は通常、テキストの長さが1つのメッセージの文字数制限を超えると警告なしに失敗するため、メッセージテキストを比較的短く保つことが重要です。


着信メッセージを受信して​​読み取るには、_"Android.provider.Telephony.SMS_RECEIVED"_アクションのBroadcastReceiverIntentFilterに登録する必要があります。このレシーバーは、マニフェストに静的に登録することも、実行時に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インスタンスの動的な登録は、コード的には管理が少し簡単です。Receiverクラスは、それを登録するコンポーネントの内部クラスにすることができ、そのコンポーネントのメンバーに直接アクセスできるためです。ただし、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();
        ...
    }
}
_

この時点で、ここでnumbersendTextMessage()呼び出しに渡されたものと比較するだけです。これにはPhoneNumberUtils.compare()を使用することをお勧めします。Receiverで取得される数値は、アドレス指定されたものとは異なる形式になる可能性があるためです。


ノート:

  • ここで示されている例は、単一パートのメッセージを使用しているため、メッセージテキストを比較的短い長さに制限する必要があります。何らかの理由で長いメッセージを送信したい場合は、代わりにsendMultipartTextMessage()メソッドを使用できます。最初に、SmsManager#divideMessage()を使用してテキストを分割し、結果のArrayListをメッセージStringの代わりにそのメソッドに渡す必要があります。 Receiverで完全なメッセージを再構成するには、各_byte[]_をSmsMessageにデコードし、メッセージ本文を連結する必要があります。

  • KitKat(APIレベル19)以降、アプリがデフォルトのメッセージングアプリではない場合、ここで使用されるメッセージはシステムとデフォルトのアプリによってSMSプロバイダーに保存されるため、プロバイダーを使用する他のすべてのアプリで利用できます。それについてできることは多くありませんが、それを本当に避けたい場合は、この同じ手法をデフォルトのアプリをトリガーしないデータSMSで使用できます。 tプロバイダーに保存されます。

    このため、sendDataMessage()メソッドが使用され、(任意の)ポート番号に追加のshort引数が必要であり、メッセージはStringではなく_byte[]_として渡されます。フィルターするアクションは_"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()をレシーバーで呼び出します。 staticオプションでは、_Android:priority="999"_属性が_<intent-filter>_の開始タグに追加されます。動的に、IntentFilter#setPriority()メソッドでも同じことができます。

    別のアプリがあなたのアプリより高い優先順位を持つことは常に可能であるので、これはまったく信頼できません。

  • これらの例では、放送局の許可を得てレシーバーを保護することを省略しました。これは、一部には単純さと明快さのためであり、一部には事物の性質が害を及ぼす可能性のあるあらゆる種類のなりすましに実際にさらされないためです。ただし、これを含める場合は、静的オプションの開始_Android:permission="Android.permission.BROADCAST_SMS"_タグに_<receiver>_属性を追加するだけです。ダイナミックの場合、registerReceiver()メソッドの4つのパラメーターのオーバーロードを使用して、その権限Stringを3番目の引数として渡し、nullを4番目の引数として渡します。

65
Mike M.