『壹』 如何在Android 4.4上實現簡訊攔截
Android在4.4上增加了不少安全措施,除了把SELinux設置為enforce外,在簡訊方向也加強了限制。
4.4之後,新增了一個default sms的機制,詳細的描述,可以參考我的另一篇文章《談談4.4中的新增功能對安全類軟體的影響》。簡而言之,就是如果要在4.4之後實現簡訊攔截功能,就必須成為default sms,把所有簡訊相關的功能都包攬了,然後再做簡訊攔截。但這種做法,適配性和兼容性的工作是非常巨大的,簡訊、wapush(多種)、彩信、單雙卡等等,相當於要求簡訊攔截類的軟體要集成一個功能非常完善的通訊錄類應用的功能。
那麼,是否有一種方法,可以在不成為default sms的同時也可以對簡訊進行「寫操作」(這可是讓4.4一下子回到解放前啊。。。。)? 答案是有的。
XDA大牛有人發現了一種比較討巧的方法,原文可以參考這里。
原理很簡單,主要是利用4.2+後的添加的App Ops許可權管理功能,在MESSAGE的TAB中找到自己的App,並進入相應的許可權管理界面,如下圖所示,FinalDemo是我自己測試的一個DEMO:
留意到Write SMS/MMS的開頭,默認是OFF的,但我們可以把它打開。
打開之後,我們就可以通過監控簡訊資料庫變化的方法實現簡訊攔截了,我也寫了個簡單的測試代碼,測試成功,把代碼和相關的配置也放了來吧
打開App Ops的代碼
[java] view plain
<span style="white-space:pre"> </span>Intent intent = new Intent(Intent.ACTION_MAIN);
ComponentName cn = new ComponentName("com.android.settings", "com.android.settings.Settings");
intent.setComponent(cn);
intent.putExtra(":android:show_fragment", "com.android.settings.applications.AppOpsSummary");
startActivity(intent);
AndroidManifest.xml的配置
[html] view plain
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.finaldemo"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="19" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
<uses-permission android:name="android.permission.RECEIVE_MMS" />
<!-- <uses-permission android:name="android.permission.SEND_SMS"/> -->
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.finaldemo.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".SmsReceiver"
android:permission="android.permission.BROADCAST_SMS" >
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<service android:name="com.example.finaldemo.SmsService" />
</application>
</manifest>
簡訊攔截的代碼
[java] view plain
mObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
ContentResolver resolver = getContentResolver();
Cursor cursor = resolver.query(Uri.parse("content://sms/inbox"), new String[] { "_id", "address", "body" }, null, null, "_id desc");
long id = -1;
if (cursor.getCount() > 0 && cursor.moveToFirst()) {
id = cursor.getLong(0);
String address = cursor.getString(1);
String body = cursor.getString(2);
Toast.makeText(SmsService.this, String.format("address: %s\n body: %s", address, body), Toast.LENGTH_SHORT).show();
}
cursor.close();
if (id != -1) {
int count = resolver.delete(Sms.CONTENT_URI, "_id=" + id, null);
Toast.makeText(SmsService.this, count == 1 ? "刪除成功" : "刪除失敗", Toast.LENGTH_SHORT).show();
}
}
};
getContentResolver().registerContentObserver(Uri.parse("content://sms/"), true, mObserver);
個人結論
在4.4上我們可以在不成為default sms的前提下實現簡訊攔截,但由於App Ops從4.3出現到4.4一直牌隱藏的狀態,猜想google還在不斷調整中,4.4之後的子版本是否會保留,是完全不能保證的;
Write SMS/MMS的許可權開關的存在跟defaultsms本身是一個矛盾,之所以出現Write SMS/MMS的許可權開關,完全是因為App Ops出現在前,而defaultsms出現在後所致;
在4.4前,簡訊攔截都是通過動態注冊高優先順序BroadcastReceiver的方式進行攔截的,主要是用於跟競品進行簡訊搶占。而現在ContenetObserver是並行通知的情況下,如果過濾邏輯不夠快,依然有可能會被競品搶先把簡訊先刪除掉,導致拿到的最後一次簡訊是舊的簡訊。建議結合BroadcastReceiver和ContenetObserver進行攔截,BroadcastReceiver做內容校正和後備數據,以防拿到的最後一條簡訊是舊的時候,依然可以進行正常的攔截流程;