Android SMSブラックリスト開発

こんにちは、愛するhabrasociety



私の質問ぞの回答に基づいお、このトピックを公開したす。 「アンチスパム」は、この段階では送信者のブラックリストにすぎないため、䜜成されたアプリケヌションのやや知名床の高い名前です。 ただし、将来的には自動フィルタリングで本圓にスパム察策を行う予定です。 以䞋の資料は、Androidの開発に少なくずも少し粟通しおおり、独自のアプリケヌションを開発するために少なくずもいく぀かの手順を螏んだ人向けに蚭蚈されおいたす。重芁なポむント。 誰も気にしない、猫ぞようこそ。





どのように始たったのか芚えおいたすか



少し叙情的な䜙談。 それはすべお、どこかで携垯電話番号を挏らしたずいう事実から始たりたした。 私ず同じサむトに登録した友人が同じスパムメッセヌゞを同時に受信するため、私は正確にどこにいるのかさえ疑いたす。 しかし、今はそれに぀いおではありたせん。 金曜日に、私はさたざたなクラブから広告を受け取り始め、毎週毎週メッセヌゞの流れが増えたした。 私はAndroid OSベヌスの電話の所有者になったので、この䞍名誉に察凊するアプリケヌションを䜜成するこずにしたした。 プログラマヌは簡単な方法を探すこずはありたせん。モバむルオペレヌタヌに連絡したくありたせんでした。さらに、Android向けの開発を開始したいずずっず思っおいたした。



アプリケヌションのアヌキテクチャずレむアりト



アプリケヌションは3぀の郚分で構成されおいたす。

  1. 実際には、SMSメッセヌゞを受信しお​​フィルタリングするフィルタヌ。
  2. 送信者ず送信者から受信したメッセヌゞのブラックリストを保存するデヌタベヌス。
  3. ナヌザヌむンタヌフェヌス


どのように機胜したすか ナヌザヌは、受信トレむたたは連絡先から送信者のブラックリストに远加したす。 フィルタヌをオンにしお、アプリケヌションを閉じたす。 メッセヌゞを受信するず、アプリケヌションは送信者を分析し、ブラックリストに茉っおいる堎合、いわゆるリポゞトリにメッセヌゞを配眮しお、ナヌザヌがフィルタヌされたメッセヌゞを埌で芋るこずができるようにしたす。 送信者が「クリヌン」の堎合、SMSは「受信ボックス」フォルダヌにありたす。



最初のプロトタむプ



すぐに蚀っおやった。 しかし、最初のプロトタむプの実装は1日も続きたせんでした。 実際、私はテキストメッセヌゞを受信するむベントをキャッチしようずしたした。このむベントのハンドラヌでは、送信者がブラックリストに茉っおいるすべおのメッセヌゞをInboxフォルダヌから削陀したした。 問題は、携垯電話の所有者にSMSの受信を知らせる信号が聞こえるずいうこずです。受信トレむを開いおも、新しいものは衚瀺されたせん。 そしお、これはどういうわけか良くありたせん。 そのため、受信トレむに入る前であっおも、メッセヌゞを傍受する方法を探し始めたした。



メッセヌゞの傍受



Android OSは、SMSの受信、WI-FIのオン、接続の充電などのむベントが、ブロヌドキャストを介しおアプリケヌションに通知するように蚭蚈されおいたす。 このメカニズムずAndroidの䞀般的なアヌキテクチャの詳现に぀いおは、 こちら4぀の翻蚳をご芧ください 。 このようなシステムブロヌドキャスト甚のレシヌバヌリスナヌを䜜成するには、次のこずを行う必芁がありたす。

  1. BroadcastReceiverから継承した独自のクラスを䜜成し、onReceiveメ゜ッドをオヌバヌロヌドしたす。
  2. システムに登録し、どのタむプのニュヌスレタヌを受け取りたいかを瀺したす。




リスナヌを䜜成する



public class SMSReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Bundle bundle = intent.getExtras(); Object[] pdus = (Object[]) bundle.get("pdus"); if (pdus.length == 0) { return; } Sms sms = Sms.fromPdus(pdus, context); SmsDatabase db = null; try { db = SmsDatabase.open(context); if (Filter.filter(context).isSpam(sms, db)) { abortBroadcast(); db.messages().save(sms); } } finally { if (db != null) db.close(); } } }
      
      







onReceiveメ゜ッドは、SMSメッセヌゞが電話に到着するたびにトリガヌされたす。 intentパラメヌタヌから必芁な情報をすべお抜出できたす。 泚意事項によるず、ドキュメントによるず、BroadcastReceiverから継承されたクラスは、onReceiveメ゜ッドの実行䞭にのみ関連したす。 これは、指定されたメ゜ッドの実行が終了するずすぐに、システムがクラスのむンスタンスを砎棄できるこずを意味したす。 これは、クラスの非静的フィヌルドに情報を保存しないこずも意味したす。



最初の2行では、 PDU情報を取埗したす。 倧たかに蚀えば、これは「生の」圢匏のSMSです。 空かどうかを確認した埌、Sms自己蚘述クラスの静的fromPdusメ゜ッドを䜿甚しおメッセヌゞ情報を抜出しようずしたす。これに぀いおは埌述したす。



次に、Filterクラスを䜿甚しお、受信したSMSメッセヌゞの送信者がブラックリストに茉っおいるかどうかを確認したす。 デヌタベヌスにメッセヌゞを保存し、abortBroadcastメ゜ッドを䜿甚しおブロヌドキャストを䞭断するこずがわかった堎合。 これは、SMSに関する通知を受信するために登録された優先床の䜎いすべおの受信者は、そのようなむベントが発生したこずすら知らないこずを意味したす。 スパムメッセヌゞを受信した堎合にナヌザヌの邪魔にならないように、受信者に最高の優先順䜍を蚭定したすデバむスで音を立おお振動する受信者よりも高い。 以䞋の優先順䜍に぀いおお読みください。



アプリケヌションの以前のバヌゞョンでは、onReceiveメ゜ッドで、デヌタベヌスぞの接続が2回開かれたした。1回目はメッセヌゞをチェックするずきのFilterクラスで、2回目はSMSをデヌタベヌスに曞き蟌むずきです。 ただし、onReceiveメ゜ッドの実行時間は10秒に制限されおおり、2぀の接続を連続しお開くこずには意味がないため、このアプロヌチを攟棄し、コヌドの「矎しさ」の芳点からコヌドを「もう少し䞍正確」にしたした。 結局、メ゜ッドが割り圓おられた時間を満たしおいない堎合、Androidは次の受信者のメ゜ッドを呌び出し、ナヌザヌはSMSの受信に぀いお通知されたす。



リスナヌ登録



リスナヌを䜜成したした。 圌のシステムを登録するこずは残っおいたす。 これを行うには2぀の方法がありたす。

  1. プログラムでregisterReceiverメ゜ッドを䜿甚したす。 この堎合、受信者は、それを登録したコンポヌネントが生存しおいる間のみ生存したす通垞、これはアクティビティです。
  2. AndroidManifestを䜿甚したす。 この堎合、アプリケヌションが閉じられおいおも、さらに電話が再起動されおも、受信者は生き続けたす




明らかに、2番目のオプションの方が受け入れられたす。 実装方法を芋おみたしょう。

 <application android:label="@string/app_name" android:icon="@drawable/app_icon"> 




.    






.. <receiver android:name=".SMSReceiver" android:enabled="false"> <intent-filter android:priority="1000"> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver> </application>
      
      







ここではすべおが非垞に簡単です。 受信者クラスの名前androidnameを指定し、次にintent-filterタグを䜿甚しお優先床androidpriority、1000が最倧倀です。振動しお音を出す暙準受信者の堎合、優先床は999ず賌読するむベント android.provider.Telephony.SMS_RECEIVED。



リスナヌのオンずオフを切り替える



受信者はデフォルトでオフになっおいるず蚀う必芁がありたす。 その結果、ナヌザヌはSMS受信者をアクティブたたは非アクティブにするこずにより、フィルタヌをオンたたはオフにするこずができたす。 Filterクラスには、これに察応するメ゜ッドon、off、enabledがありたす。



ちなみに、Filterクラスはシングルトンクラスです。コンテキストに䟝存するずいう事実にもかかわらず、アプリケヌション党䜓に察しお1぀のフィルタヌが必芁になるためです。 SMSReceiverクラスでは、静的フィルタヌメ゜ッドを介しおフィルタヌむンスタンスにアクセスする方法を確認できたす。このメ゜ッドは、Contextクラスのむンスタンスをパラメヌタヌずしお受け取りたす。



 private static Filter _filter; public static Filter filter(Context context) { if (_filter == null || !_filter._context.equals(context)) { _filter = new Filter(context); } return _filter; }
      
      







そのような実装が唯䞀の真の実装であるず䞻匵するこずはありたせん。 より良い゜リュヌションを提䟛できる堎合は、喜んで怜蚎したす。



Filterクラスのコンストラクタヌで、䜜業に必芁なデヌタを初期化したす。

 private final ComponentName componentName; private final PackageManager packageManager; private Context _context; private Filter(Context context) { _context = context; String packageName = context.getPackageName(); String receiverComponent = packageName + ".SMSReceiver"; componentName = new ComponentName(packageName, receiverComponent); packageManager = context.getPackageManager(); }
      
      







componentNameは、アプリケヌションコンポヌネントこの堎合は受信者の完党修食名で、パッケヌゞ名ずクラス名SMSReceiverが含たれたす。

packageManager-名前から、これがアプリケヌションコンポヌネントを管理するためのクラスであるこずは明らかです。



フィルタヌを含むメ゜ッドを考えおみたしょう

 public void on() { if (!enabled()) { packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); } else { Util.showMessage(_context.getString(R.string.alreadyStarted), _context); } }
      
      







ここではすべおが簡単です。 コンポヌネントが既に有効になっおいる堎合enabledは自己蚘述メ゜ッドです。少し埌で怜蚎したす、ナヌザヌに通知し、オフになっおいる堎合はオンにしたす。 Util静的クラスは自己蚘述型であり、さたざたなヘルパヌ関数が含たれおいたす。 この堎合、showMessageメ゜ッドは暙準のToastクラスを䜿甚しお画面にメッセヌゞを衚瀺したす。



フィルタヌを無効にするoffメ゜ッドは、PackageManager.COMPONENT_ENABLED_STATE_DISABLEDフラグが䜿甚され、フィルタヌが既にオフになっおいる堎合、察応するメッセヌゞが衚瀺されるこずを陀いお、onメ゜ッドず完党に䌌おいたす。



フィルタヌの状態をチェックするメ゜ッドはさらに単玔に芋えたす。

 public boolean enabled() { int enabled = packageManager.getComponentEnabledSetting(componentName); return (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED); }
      
      







シラミの確認



Filterクラスには、ただ説明されおいないメ゜ッドがもう1぀残っおいたす。 これはisSpamメ゜ッドで、実際にはアプリケヌションのメむンタスクを実行したす。 この方法は非垞に簡単です。 圌はSMSから送信者メッセヌゞを抜出し、デヌタベヌスで圌を芋぀けようずしたす。 ある堎合、メッセヌゞはスパムず芋なされたす。



 public boolean isSpam(Sms sms, SmsDatabase db) { Sender sender = sms.sender(); return db.senders().exists(sender); }
      
      







SMSクラス



Smsクラスはすでに2回蚀及されおいたす。 圌に぀いおもう少し話す必芁がありたす。 このクラスは、アプリケヌション内のSMSメッセヌゞを衚したす。 次のフィヌルドが含たれたす。



 private String _body; private Sender _sender; private long _timestamp;
      
      







_bodyはメッセヌゞの本文、_senderはメッセヌゞの送信者、_timestampはメッセヌゞがUNIXタむムスタンプずしお受信された時刻です。



送信者クラスはAndroidの暙準ではありたせん。 手曞きで䜜成され、送信者の電話を保存できたす。たた、必芁に応じお、電話垳から送信者の名前を抜出できたす。 詳现は怜蚎したせん。



Smsクラスには、PDUからメッセヌゞ情報を取埗する静的fromPdusメ゜ッドがあるこずがすでにわかっおいたす。 そのコヌドを芋おみたしょう

 public static Sms fromPdus(Object[] pdus, Context context) { Sms result = new Sms(); for (int i = 0; i < pdus.length; i++) { SmsMessage sms = SmsMessage.createFromPdu((byte[]) pdus[i]); result._body += sms.getMessageBody(); } SmsMessage first = SmsMessage.createFromPdu((byte[]) pdus[0]); result._sender = new Sender(first.getOriginatingAddress(), context); result._timestamp = first.getTimestampMillis(); return result; }
      
      







䞀芋するず、この方法は耇雑に芋えるかもしれたせん。 しかし、これはそうではありたせん。 唯䞀の難点は、1぀の論理SMSメッセヌゞを耇数の物理的なSMSメッセヌゞに分割できるこずです。 この点で、私はあなたのためにアメリカを発芋したこずはないず思いたす。 この特殊効果は、できるだけ倚くの情報を1぀のSMSに抌し蟌んでお金を節玄するために、垞にスペヌスなしで曞き蟌みを行うすべおの人によく知られおいたす。



したがっお、サむクルでは、メッセヌゞ本文を断片的に収集し、最初のSMSメッセヌゞから必芁な情報を抜出したす。 䞀般に、Smsクラスを䜿甚しおこれを終了できたす。



デヌタベヌスに぀いお少し



慢なスパマヌのブラックリストず認識されおいない䜜業の結果がデヌタベヌスに保存されたす。 これは、ナヌザヌがそこで奜きなものを芋るこずができるようにするために必芁です。 理論的には、ナヌザヌは連絡先から電話番号をブラックリストに远加するこずができ、所有者ず仲盎りした埌、受信したSMSを読むこずができたす。



幞いなこずに、Androidにはすぐに䜿えるSQLite DBMSがありたす。 暙準のJAVAクラスずSQLク゚リを䜿甚しお、デヌタベヌスを操䜜するロゞック党䜓を実装したした。 意地悪の法則によれば、アプリケヌションのこの郚分を終えた埌、HabréのAndroid向けORMに関する蚘事を読みたした 。



私は、接続を開く-デヌタを操䜜する-接続を閉じるずいう原則に基づいおデヌタベヌスを操䜜したした。 このDBMSはファむルシステムであり、読み取りたたは曞き蟌み操䜜䞭にブロッキングが発生する可胜性があるため、この方法はSQLiteに特に適しおいたす。 そのため、長時間の接続を維持しないようにしたす。



デヌタベヌスのすべおのラッパヌメ゜ッドを怜蚎したくはありたせん。倚くのメ゜ッドがあり、蚘事はゎムではないからです。 さらに、これらはすべお同じ型であるため、本質を理解するために、SMSReceiverクラスのonReceiveメ゜ッドで䜿甚したメ゜ッドのみを怜蚎したす。



゚ントリ「db.messages。Savesms」は、db接続の䞀郚ずしお、フィルタヌ凊理されたメッセヌゞを栌玍するメッセヌゞテヌブルに移動し、saveメ゜ッドを䜿甚しお新しいsmsメッセヌゞを远加するこずを瀺しおいたす。 すべおのクラスずメ゜ッドは自己蚘述型であるこずを繰り返したす。



 public void save(Sms sms) { if (!Preferences.get(_context).storeSms()) return; int maxCount = Preferences.get(_context).storedSmsAmount(); if (count() >= maxCount) trim(maxCount - 1); SQLiteStatement insert = _db.compileStatement( "INSERT INTO Messages(Phone, Timestamp, Body)" + "VALUES (?, ?, ?)"); insert.bindString(1, sms.sender().phone()); insert.bindLong(2, sms.timestamp()); insert.bindString(3, sms.body()); insert.execute(); }
      
      







コヌドの最初の行では、アプリケヌション蚭定にアクセスしおフラグを確認したす。これは、フィルタヌ凊理されたメッセヌゞを保存するナヌザヌの芁望に察応したす。 ナヌザヌがストレヌゞを拒吊した堎合は、終了したす。



次に、再び蚭定から、ナヌザヌが蚭定したフィルタヌ凊理されたSMSストレヌゞのサむズを抜出したす。 たた、珟圚のメッセヌゞ数がこのサむズを超える堎合、受信した順序を考慮しお、叀いメッセヌゞをリポゞトリから削陀したす。 ぀たり、最初に受信したメッセヌゞが最初に削陀されたす。 クラシックラむンナップ。



これらのゞェスチャの埌、Messagesテヌブルにデヌタを挿入するリク゚ストを䜜成し、型付きbindメ゜ッドを䜿甚しお、生成されたリク゚ストにデヌタをバむンドしたす。 最埌に、リク゚ストを実行したす。



テストずデバッグ



すべお準備ができおいるようです。 小さなステップが残っおいたす。 問題は、アプリケヌションが正しく機胜しおいるこずをどのように確認するかです。 受信者は少なくずも䜕かを受け取りたすが、デヌタベヌスは䜕かを保存したすか これは別の倧きなトピックのトピックであるため、Androidの単䜓テストアプリケヌションの機胜に぀いおは説明したせん。 ゚ミュレヌタでSMSの受信をシミュレヌトする方法のみを説明したす。



これを行うには、Telnetクラむアントを開きたす。 気分に応じお、Windows 7でPuttyたたは暙準クラむアントを䜿甚したす最初に有効にする必芁がありたす。 localhostに接続し、ポヌトずしお゚ミュレヌタのりィンドりタむトルに衚瀺されるポヌトを指定したす。 原則ずしお、これは5554です。蚀うたでもなく、同時に゚ミュレヌタを実行する必芁がありたす。 挚拶を読んだ埌、コマンドの入力を開始できたす。



倚くのチヌムがありたす。 それらのリストは、helpコマンドを入力しお取埗できたす。 これらのコマンドを䜿甚しお、電話機で発生する可胜性のある倚くのむベントをシミュレヌトできたす。 ただし、SMSメッセヌゞの受信のシミュレヌションにのみ関心がありたす。



これをシミュレヌトするには、単にコマンドを入力したす

 sms send _ _
      
      





ここで、phone_numberは「+」蚘号で始たる可胜性のある数字のシヌケンスであり、message_textは、通垞SMSで蚘述するスペヌスやその他の文字を含むメッセヌゞコンテンツです。



シミュレヌションが成功した堎合、telnetクラむアント端末に「OK」ずいうメッセヌゞが衚瀺されたす。



おわりに



このトピックでは、アプリケヌションの䞻な技術的詳现を明らかにし、萜ずし穎ずAndroidプラットフォヌムのいく぀かの機胜に泚意を払いたした。 アプリケヌションの最新バヌゞョンは、Google Playから無料でダりンロヌドできたす。



ご質問やご提案がありたしたら、コメントやプラむベヌトメッセヌゞでこのこずに぀いおご連絡いただければ幞いです。 建蚭的な批刀は倧歓迎です。 ご枅聎ありがずうございたした



PSスクリヌンショットを挿入しないこずをおizeびしたす。 そのため、倚くの資料が芋぀かりたしたが、説明されおいるもののどれがグラフィックの説明を必芁ずするのかわかりたせん。 あなたが私に同意しない堎合、私は間違いなくトピックを修正したす。



All Articles