android 中管理短信

转载 2011年01月19日 17:10:00

为了看代码方便,一边在网上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    

检索数据方法很简单:  

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

 


 

系统连最起码的数据校验都没有做啊,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还是有所控制的。

不过支持--也很不错了,这样可以查询数据库中所有的表,而且还可以多表联查

Android开发之SmsManager(短信管理器)详解

SmsManager是Android提供的另一个非常常见的服务,SmsManager提供了系列sendXxxMessage()方法用于发送短信。 SmsManager:管理短信操作,如发送数据,文本和...
  • fengyuzhengfan
  • fengyuzhengfan
  • 2014年07月27日 09:24
  • 3373

Android短信接收过程源码分析(原)

本文主要讨论RILJ接收到一条普通短消息时是如何把它转换成Broadcast发送到系统中的。 一、GsmSMSDispatcher注册监听过程         在《Framework层中的...
  • u010961631
  • u010961631
  • 2013年10月14日 10:53
  • 5108

android学习十四(android的接收短信)

android中短信的发送也接收拦截
  • wj903829182
  • wj903829182
  • 2014年11月18日 10:53
  • 2262

android短信发送和接受

短信发送发短信第一件事是在manifest文件中添加权限 第一方法:调用系统发短信的应用 /** * 调起系统功能发短信 * @param phoneNu...
  • JQ_AK47
  • JQ_AK47
  • 2016年09月19日 13:42
  • 1436

Android 读取所有短信

首先在AndroidManifest.xml 的manifest标签下添加权限。 通过短信数据库读取短信内容private Uri SMS_INBOX...
  • OOC_ZC
  • OOC_ZC
  • 2017年05月06日 11:12
  • 2619

Android短信发送流程之长短信发送(原)

从前面《Android短信发送流程之普通短信发送》流程看到,长短信与普通短信的流程从SmsManager的sendMultipartTextMessage()方法开始区分,现在我们来看长短信的流程: ...
  • u010961631
  • u010961631
  • 2015年12月12日 08:41
  • 2297

Android开发之监听或获取手机短信内容

最近做的项目里要监听手机的短消息内容,这个东西好像在最初学的时候都做过,不过现在早忘记了,现在写下来,记住了。 前提--权限: [java] view plain ...
  • zhuiqiuk
  • zhuiqiuk
  • 2016年12月12日 22:02
  • 1386

Android解析短信信息

最近做项目要解析手机中短信,获取短信有两种方式:一种是通过广播另一种就是通过数据库,由于项目需要我选择的是第二种方式,检索数据库来获取短信信息并且进行解析指定内容的短信。 在做任何一个新的功能的时候...
  • jwzhangjie
  • jwzhangjie
  • 2014年01月17日 21:56
  • 4197

Android实战技巧之三十九:短信收发

7月4日从广州出差回来就定下写作计划,但迟迟没有动笔。耽搁的原因还是老样子,工作上又有新任务,全部精力都投入过去了,每天精疲力竭的回来也打不起精神做其他事了。这就是精力管理不当所致,就像我把很多要做的...
  • lincyang
  • lincyang
  • 2015年08月15日 21:57
  • 9722

Android 短信发送流程

短信发送Framework流程,源代码的版本是4.4 1、通过Mms源代码可以知道短信发送最后调用的关键api如下: SmsSingleRecipientSender.java ...
  • gaojinshan
  • gaojinshan
  • 2015年04月07日 20:16
  • 2436
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章: android 中管理短信
举报原因:
原因补充:

(最多只允许输入30个字)