android 管理手机短信
为了看代码方便,一边在网上google资料,一边看Android java 源代码。
偶然发现了一个类MmsSmsDatabaseHelper.java,原来android将所有的短信信息都存入了mmssms.db中。
公开的SDK中没有这个类,不能直接使用。于是自己写了一个SQLiteOpenHelper,但是查询的时候发生SQL异常。
看来不能为所欲为了,不过据网上资料介绍可以拷贝db文件来实现短信数据备份。
既然每个db跟package名相关,建立了一个package为com.android.providers.telephony的工程去试一试,看看能不能成功。
结果输出Please execute 'adb uninstall com.android.providers.telephony' in a shell,android的安全做得很强大啊。
不能直接访问数据库,只能通过协议来访问数据库了,
先贴出相关的协议:
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
检索数据方法很简单:
|
1
2
3
4
5
6
7
8
9
10
|
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中加入权限:
|
1
|
<uses-permission android:name="android.permission.WRITE_SMS"></uses-permission> |
然后删除短信:
|
1
|
this.getContentResolver().delete(Uri.parse("content://sms"), "_id=?", new String[]{"3"}); |
删除成功。
Url中content://sms 替换成content://sms/ 也成功,但是其它url时程序报错,比如content://sms/inbox
看了一下android的源代码,sms支持的协议有:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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测试一下:
|
1
2
3
4
5
6
|
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
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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
系统连最起码的数据校验都没有做啊,google对程序员也太仁慈了。
看看能不能再挖掘一下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);
很可惜,只运行了第一条SQL语句,看来在关键问题上,android还是有所控制的。
不过支持--也很不错了,这样可以查询数据库中所有的表,而且还可以多表联查
原文转自:http://blog.csdn.net/rangq1/article/details/5802076
android 管理手机短信的更多相关文章
- Android 读取手机短信
获取android手机短信需要在AndroidManifest.xml加权限: <uses-permission android:name="android.permission.RE ...
- android 添加手机短信,获取手机短信,删除手机短信和修改手机短信
注意添加权限: <uses-permission android:name="android.permission.READ_SMS"></uses-permis ...
- Android(java)学习笔记247:ContentProvider使用之利用ContentProvider备份和还原手机短信(掌握)
1.通过阅读系统源码我们知道: 短信的内容提供者: content://sms/ 系统短信的内容提供者的路径 2. 利用ContentProvider备份和还原手机短信: (1) ...
- android 监听短信数据库,制作短信控制工具,控制别人的手机!!(一)
序言:本程序示例本着简洁易懂的目的,只做了简单的功能实现,需要用户启动应用,收到短信才有效果.作者将会在后面的(二)篇中加入服务后台运行.自动启动功能,实现一个真正的短信控制工具.本文的目的很简单,让 ...
- android手机短信获取
关于Android中对短信的一些相关操.我看到一个文章下面我就从标题中的三个方面来对Android系统中的短信操作进行一个简单地学习. 短信发送: 由于Android中对短信发送方法的优良封装,之后对 ...
- Android(java)学习笔记191:ContentProvider使用之利用ContentProvider备份和还原手机短信(掌握)
1. 通过阅读系统源码我们知道: 短信的内容提供者: content://sms/ 系统短信的内容提供者的路径 2. 利用ContentProvider备份和还原手机短信: (1 ...
- 用Tasker实现收到Android手机短信自动转发到邮箱
发送短信到邮箱的原理与 <用Tasker实现收到Android手机短信自动转发到邮箱>有些类似. 发送短信到邮箱是利用Ifttt这个服务将短信转发到邮箱中.Ifttt服务的可扩展性很强, ...
- Android之发送短信的两种方式
SMS涉及的主要类SmsManager 实现SMS主要用到SmsManager类,该类继承自java.lang.Object类,下面我们介绍一下该类的主要成员. 公有方法: ArrayList< ...
- (转)Android之发送短信的两种方式
https://www.cnblogs.com/dongweiq/p/4866022.html if(TextUtils.isEmpty(number)||TextUtils.isEmpty(cont ...
随机推荐
- Oracle学习笔记:decode函数
decode函数主要作用:将查询结果翻译成其他值(即以其他形式变现出来) 使用方法: SELECT DECODE(colunm_name,值1,翻译值1,值2,翻译值2……值n,翻译值n,缺省值) F ...
- MySQL 连接本地数据库、远程数据库命令
一.MySQL 连接本地数据库,用户名为“root”,密码“123”(注意:“-p”和“123” 之间不能有空格) C:/>mysql -h localhost -u root -p123 二. ...
- OA项目Spring.Net代替抽象工厂(三)
Servrvice层的代码: <?xml version="1.0" encoding="utf-8" ?> <objects xmlns=& ...
- 微信小程序-视频教程-百度云-下载
链接: https://pan.baidu.com/s/16WGL3whutozx-UXqsDPhhA 提取码: 关注公众号[GitHubCN]回复获取 什么是微信小程序?小程序是一种不需要下载安 ...
- Signalr信息推送
前序 距离上次写文章,差不多已经大半年了.感觉自己越来越懒了,即使有时候空闲下来了,也不想动.前面买了一系列的Python的书,基础的看了大概有四分之一,剩下的基本上还未动,晚上回去也只是吃饭看电影. ...
- spring boot之org.springframework.boot.context.TypeExcludeFilter
曾经碰到过这样一种情况,想让某个使用了spring 注解的类不被spring扫描注入到spring bean池中,比如下面的类使用了@Component和@ConfigurationPropertie ...
- ORACLE TO_CHAR(SYSDATE,'D')
DDD是该天在一年内的第多少天,d是在一周内第几天,dd是一个月内的 DY :Day of week abbreviated Mon, Tue, Fri DAY :Day of week spelle ...
- 纯CSS实现3D图像轮转
CSS演武场今天继续,今天看一个纯css实现的3D图像轮转效果,请大家猛戳研究效果先,也可下载收藏先. 首先看html文件,div.billboard为效果的容器,利用10个div.poster分割图 ...
- 大数据技术之_16_Scala学习_04_函数式编程-基础+面向对象编程-基础
第五章 函数式编程-基础5.1 函数式编程内容说明5.1.1 函数式编程内容5.1.2 函数式编程授课顺序5.2 函数式编程介绍5.2.1 几个概念的说明5.2.2 方法.函数.函数式编程和面向对象编 ...
- Java 集合之 Collection
集合就是一组数的集合,就像是一个容器,但是我们应该清楚的是集合中存放的都是对象的引用,而不是真正的实体.而我们常说的集合中的对象其实指的就是对象的引用. 我们可以把集合理解为一个小型数据库,用于存放数 ...