Android_通过ContentObserver监听短信数据变化

1.简介

在小米等一些机型,无法接收系统发出的短信广播。只能通过观察者ContentObserver,去监听短信数据的变化

2.SMS数据介绍

content://sms/inbox        收件箱

content://sms/sent        已发送

content://sms/draft        草稿

content://sms/outbox        发件箱

content://sms/failed        发送失败

content://sms/queued        待发送列表

在模拟器上Outbox没有查询到数据,在模拟器上找了老半天也没找到发件箱,很郁闷。

数据库中sms相关的字段如下:

_id               一个自增字段,从1开始

thread_id    序号,同一发信人的id相同

address      发件人手机号码

person        联系人列表里的序号,陌生人为null

date            发件日期

protocol      协议,分为: 0 SMS_RPOTO, 1 MMS_PROTO

read           是否阅读 0未读, 1已读

status         状态 -1接收,0 complete, 64 pending, 128 failed

type

ALL    = 0;

INBOX  = 1;

SENT   = 2;

DRAFT  = 3;

OUTBOX = 4;

FAILED = 5;

QUEUED = 6;

body                     短信内容

service_center     短信服务中心号码编号

subject                  短信的主题

reply_path_present     TP-Reply-Path

locked

检索数据方法很简单:

Uri uri = Uri.parse("content://sms/inbox");

Cursor cur = this.managedQuery(uri, null, null, null, null);

if (cur.moveToFirst()) {

do{

for(int j = 0; j < cur.getColumnCount(); j++){

info = "name:" + cur.getColumnName(j) + "=" + cur.getString(j);

Log.i("====>", info);

}

}while(cur.moveToNext());

}

managedQuery最终也要将参数转换为SQL语句向SQLite发送消息,因此参数跟SQL语句很类似,所以可以在查询字段中加入SQL函数,

比如new String[] projection = new String[]{"count(*) as count"}等等。

managedQuery中的参数依次为uri,

查询字段          查询字段数组,也可以将所有需要查询的字段放入一个字符内

比如new projection[]{"_id", "thread_id"}和new projection[]{"_id,thread_id"}是一致的。

跟SQL一样,字段名不区分大小写

条件                不带Where的SQL 条件字符,如果有参数则用?替代,比如"_id=? And thread_id = ? Or type = ‘1‘"

条件中的参数   参数字符数组,跟上述的条件一一对应

排序                不带Order by排序字符串,比如_id desc, type

如果参数为null,SQL中查询字段为“*”,相关的条件为空白

还可以用getContentResolver()获得一个ContentResolver,

getContentResolver().query()同样返回一个Cursor对象,参数跟managedQuery一致。

不过用ContentResolver对象去更新、删除和插入一条数据时报SecurityException。看来没有权限,在Manifest.xml中加入权限:

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

然后删除短信:

this.getContentResolver().delete(Uri.parse("content://sms"), "_id=?", new String[]{"3"});

删除成功。

Url中content://sms 替换成content://sms/ 也成功,但是其它url时程序报错,比如content://sms/inbox

看了一下android的源代码,sms支持的协议有:

sURLMatcher.addURI("sms", null, SMS_ALL);

sURLMatcher.addURI("sms", "#", SMS_ALL_ID);

sURLMatcher.addURI("sms", "inbox", SMS_INBOX);

sURLMatcher.addURI("sms", "inbox/#", SMS_INBOX_ID);

sURLMatcher.addURI("sms", "sent", SMS_SENT);

sURLMatcher.addURI("sms", "sent/#", SMS_SENT_ID);

sURLMatcher.addURI("sms", "draft", SMS_DRAFT);

sURLMatcher.addURI("sms", "draft/#", SMS_DRAFT_ID);

sURLMatcher.addURI("sms", "outbox", SMS_OUTBOX);

sURLMatcher.addURI("sms", "outbox/#", SMS_OUTBOX_ID);

sURLMatcher.addURI("sms", "undelivered", SMS_UNDELIVERED);

sURLMatcher.addURI("sms", "failed", SMS_FAILED);

sURLMatcher.addURI("sms", "failed/#", SMS_FAILED_ID);

sURLMatcher.addURI("sms", "queued", SMS_QUEUED);

sURLMatcher.addURI("sms", "conversations", SMS_CONVERSATIONS);

sURLMatcher.addURI("sms", "conversations/*", SMS_CONVERSATIONS_ID);

sURLMatcher.addURI("sms", "raw", SMS_RAW_MESSAGE);

sURLMatcher.addURI("sms", "attachments", SMS_ATTACHMENT);

sURLMatcher.addURI("sms", "attachments/#", SMS_ATTACHMENT_ID);

sURLMatcher.addURI("sms", "threadID", SMS_NEW_THREAD_ID);

sURLMatcher.addURI("sms", "threadID/*", SMS_QUERY_THREAD_ID);

sURLMatcher.addURI("sms", "status/#", SMS_STATUS_ID);

sURLMatcher.addURI("sms", "sr_pending", SMS_STATUS_PENDING);

sURLMatcher.addURI("sms", "sim", SMS_ALL_SIM);

sURLMatcher.addURI("sms", "sim/#", SMS_SIM);

其中,delete方法中支持的协议为:

SMS_ALL               根据参数中的条件删除sms表数据

SMS_ALL_ID         根据_id删除sms表数据

SMS_CONVERSATIONS_ID     根据thread_id删除sms表数据,可以带其它条件

SMS_RAW_MESSAGE              根据参数中的条件删除 raw表

SMS_STATUS_PENDING         根据参数中的条件删除 sr_pending表

SMS_SIM                                 从Sim卡上删除数据

试一下SMS_CONVERSATIONS_ID:"content://sms/conversations/3 ",删除thread_id="3", _id="5"的数据

在eclipse中的Emulator Control中,以13800给模拟器发送三条数据,然后以13900发送一条

this.getContentResolver().delete(Uri.parse("content://sms/conversations/3"), "_id=?", new String[]{"5"});

成功删除一条数据。

在数据库中每个发送者的thread_id虽然一样,但不是固定的,如果把一个发送者的全部数据删除掉,

然后换一个新号码发送短信时,thread_id是以数据库中最大的id+1赋值的。

update支持的协议有很多:

SMS_RAW_MESSAGE

SMS_STATUS_PENDING

SMS_ALL

SMS_FAILED

SMS_QUEUED

SMS_INBOX

SMS_SENT

SMS_DRAFT

SMS_OUTBOX

SMS_CONVERSATIONS

SMS_ALL_ID

SMS_INBOX_ID

SMS_FAILED_ID

SMS_SENT_ID

SMS_DRAFT_ID

SMS_OUTBOX_ID

SMS_CONVERSATIONS_ID

SMS_STATUS_ID

以SMS_INBOX_ID测试一下:

ContentValues cv = new ContentValues();

cv.put("thread_id", "2");

cv.put("address", "00000");

cv.put("person", "11");

cv.put("date", "11111111");

this.getContentResolver().update(Uri.parse("content://sms/inbox/4"), cv, null, null);

太强了,连thread_id都可以修改。

insert支持的协议:

SMS_ALL

SMS_INBOX

SMS_FAILED

SMS_QUEUED

SMS_SENT

SMS_DRAFT

SMS_OUTBOX

SMS_RAW_MESSAGE

SMS_STATUS_PENDING

SMS_ATTACHMENT

SMS_NEW_THREAD_ID

向sms表插入数据时,type是根据协议来自动设置,

如果传入的数据中没有设置date时,自动设置为当前系统时间;非SMS_INBOX协议时,read标志设置为1

SMS_INBOX协议时,系统会自动查询并设置PERSON

threadId为null或者0时,系统也会自动设置

一直为造不了"发送失败"的邮件而发愁,现在来做一个:

content://sms/failed

ContentValues cv = new ContentValues();

cv.put("_id", "99");

cv.put("thread_id", "0");

cv.put("address", "9999");

cv.put("person", "888");

cv.put("date", "9999");

cv.put("protocol", "0");

cv.put("read", "1");

cv.put("status", "-1");

//cv.put("type", "0");

cv.put("body", "@@@@@@@@@");

this.getContentResolver().insert(Uri.parse("content://sms/failed"), cv);

type被设置成了5,thread_id设置为1

看看能不能再挖掘一下sms的功能。先来做一个错误的查询:

getContentResolver().query( Uri.parse("content://sms/") , new String[]{"a"}, "b", null, null);

log输出错误的SQL语句:

SELECT a FROM sms WHERE (b) ORDER BY date DESC

query方法中没有Group by,如果想对短信做统计,对Cursor进行遍历再统计也太慢了。

在SQL语言中group by在Where后面,那就在条件参数中想想办法:

Android组织SQL语句时将条件两端加(),那就拼一个group by出来吧:

getContentResolver().query( Uri.parse("content://sms/") , new String[]{"count(*) as count, thread_id"}, "1=1) group by (thread_id", null, null);

那么输出的SQL= SELECT count(*) as count, thread_id FROM sms WHERE ( 1=1) group by (thread_id ) ORDER BY date DESC

如果想查询URI没有对应的表怎么办呢,比如想知道 mmssms.db数据库中有哪些表,

查询的表是URI定的,再在条件参数中拼凑肯定是不行。

那我们把目光往前移,看看在字段参数中能不能凑出来。

要查询其它表,关键要去掉系统固定添加的FROM sms,

用用SQL中的注释吧,

getContentResolver().query(Uri.parse("content://sms/"), new String[]{" * from sqlite_master WHERE type = ‘table‘ -- "}, null, null, null);

那么输出的SQL=SELECT * from sqlite_master WHERE type = ‘table‘ -- FROM sms ORDER BY date DESC 居然能够运行。

得寸进尺,再进一步,如果加入“;”也能运行的话,哈哈,那么建表、删除表、更新表也能为所欲为咯。

getContentResolver().query(Uri.parse("content://sms/"), new String[]{" * from sms;select * from thrreads;-- "}, null, null, null);

3.实例

public class SMSContentObserver extends ContentObserver {
	private Context mContext;
	String[] projection = new String[] { "address", "body", "date", "type", "read" };

	public SMSContentObserver(Context context, Handler handler) {
		super(handler);
		mContext = context;
	}

	@Override
	public void onChange(boolean selfChange) {
		Uri uri = Uri.parse("content://sms/inbox");
		Cursor c = mContext.getContentResolver().query(uri, null, null, null, "date desc");
		if (c != null) {
			while (c.moveToNext()) {
				SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
				Date d = new Date(c.getLong(c.getColumnIndex("date")));
				String date = dateFormat.format(d);
				StringBuilder sb = new StringBuilder();
				sb.append("发件人手机号码: " + c.getString(c.getColumnIndex("address")))
						.append("信息内容: " + c.getString(c.getColumnIndex("body")))
						.append(" 是否查看: " + c.getInt(c.getColumnIndex("read")))
						.append(" 类型: " + c.getInt(c.getColumnIndex("type"))).append(date);
				Log.i("xxx", sb.toString());
			}
			c.close();
		}
	}
}

Android_通过ContentObserver监听短信数据变化

时间: 2024-10-05 19:42:45

Android_通过ContentObserver监听短信数据变化的相关文章

Android 监听短信2种方式:Broadcast和ContentObserver

1. 基于Broadcast接受短信 1.1 原理 Android收到短信后系统会发送一个android.provider.Telephony.SMS_RECEIVED广播.把它放在Bundle(intent.Extras)中,Bundle可以理解为一个Map,短信采用"pdus"作为键,pdus应该是protocol description units的简写,也就是一组短信.Android不是一接收到短信就立刻发出广播的,他会有一定的延迟,所以就有可能会有多条短信,所以才会用数组来存

android菜鸟学习笔记23----ContentProvider(三)利用内置ContentProvider监听短信及查看联系人

要使用一个ContentProvider,必须要知道的是它所能匹配的Uri及其数据存储的表的结构. 首先想办法找到访问短信及联系人数据的ContentProvider能接受的Uri: 到github上找对应ContentProvider的源码:https://github.com/android 有好多个,哪一个才是短信数据的ContentProvider呢? 在filters输入框:输入telephony. 现在只有一个了,打开: 装有git的话,可以选择clone到本地,没有的话,就选择下载

(七)android开发中两种方式监听短信的原理和实现

一.监听短信的两种方式的简介 Android程序开发中,有两种方式监听短信内容:一.接收系统的短信广播:二.应用观察者模式,监听短信数据库. 第一种方式接收系统的短信广播: A.这种方式只对新收到的短消息有效,运行代码,并不会读取收件箱中已读或未读的消息,只有当收到新来的短消息时,才会执行onReceive()方法. B.并且这个广播是有序广播,如果当别的程序先读取到了这个广播,然后拦截掉了个这个广播,你将接收不到.当然我们可以通过设置priority的数值,其实有时是不管用的,现在在一些定制的

Android 监听短信 两种方式

1. 接受系统的短信广播,操作短信内容. 优点:操作方便,适合简单的短信应用. 缺点:来信会在状态栏显示通知信息. AndroidManifest.xml: <uses-permission android:name="android.permission.SEND_SMS"></uses-permission> <uses-permission android:name="android.permission.RECEIVE_SMS"

Android 监听短信(同时监听广播和数据库)

暗扣,强烈谴责这种侵害用户利益的行为... 下面给大家介绍Android暗扣原理.......  Android4.4以下的系统玩游戏就要小心了哈 暗扣方式之一:短信订购,即监听--------拦截------------处理短信. 暗扣方式之二:模拟人为操作(又叫模拟流量),通过后台程序代码模拟人的点击行为,暗自给用户订购业务,由运营商收取你的费用,当然这其中也需要涉及监听/拦截/处理短信.使用这种方式的原理无非是Http处理网页,还涉及接入点切换问题,这里就不详细讲解. 回归正题:有的时候,

android 监听短信并发送到服务器

1. 接受系统的短信广播,操作短信内容. 优点:操作方便,适合简单的短信应用. 缺点:来信会在状态栏显示通知信息. 2. 应用观察者模式,监听短信数据库,操作短信内容.   实例如下: SystemEventReceiver:收到开机启动OK的广播后,启动BootService服务:收到应用发送短信的INTENT后,调用系统接口发送短信 BootService:开启服务后,打开短信监听器 SMSObserver:数据观察者,监听短信数据库 SMSHandler:短信处理器

Android实战简易教程-第三十六枪(监听短信)

一般用户喜欢用手机号作为用户名注册APP账号,这时一般都是通过手机验证码的方式进行验证,下面我们就研究一个非常实用的方法,通过监听短信-实现短信验证码的自动填入,提高用户体验. 首先我们看一下如何监听手机短信. 一.获取短信全部内容 1.新建一个SMSBroadcastReceiver: <code class="hljs java has-numbering"><span class="hljs-keyword">package</s

监听短信(监听系统广播)

监听短信(监听系统广播)

通过broadcastreceiver 监听短信问题

在mainfest中 订阅   短信到来的广播时候  发现找不到 <action android:name="android.provider.Telephony.SMS_RECEIVED"> 目前未找到原因, 不知道,采用通过代码订阅广播方式 会不会成功 目前尚未验证 在网上人的回答 链接:https://www.zhihu.com/question/28521646/answer/64531805来源:知乎 1   因为这个广播很容易被不法分子拿来使用. google公