在framework层下的SmsManager类中,封装好了一个copyMessageToIcc方法,只要正确地调用它便可以将短信存储到SIM卡中。
SmsManager smsManager = SmsManager.getDefault(); //用来获取一个SmsManager对象
现在我们来看一下copyMessageToIcc(byte[] smsc, byte[] pdu, int status)这个方法的三个参数:
1) byte[] smsc : 短信服务中心的地址,个人认为在复制到SIM卡过程中可以为空。
2) byte[] pdu : 中文翻译是协议数据单元,这个参数最为重要,一会我们会做详细地解释说明。
3) int status : 短信存储在Icc卡上的状态,有4种状态,1是已读,3是未读,5是已发送,7是未发送。
其实要想将将短信正确地存储到SIM卡上,pdu这个参数尤为重要,下面我们就来分析一下!
首先先看一下复制短信到SIM卡时log显示的正确pdu:
00 00 0d91683155724572f9 00 0b 11117091914323 0a4e0b73ed4e864e48ff1f
原本上面的pdu是完整连续,为了方便解释说明,特意加了空格区分开来。
00 SC Address 短信服务中心地址,通常我们发送短信时会发送一个pdu到短信服务中心,然后短信服务中心会对pdu进行一些处理再发送到目的手机,这其中就包括增加了这个SC Address和后面会介绍的时间戳。
00 PDUType pdu的第一个八位位组,即一个八位的二进制数转变成十六进制而来,每一位代表什么意思呢?由高到低依次代表RP(应答路径)、UDHI(用户数据头标识)、SRR(状态请求报告)、VPF(有效期格式,2位)、RD(拒绝副本)、MTI(信息指示类型,2位),在这里我们其实只要全部将其设置为0就好,于是便显示为00。
0d91683155724572f9 这一段代表了目的手机的号码,0d代表后面的地址长度(二进制下为13),那么这十三个数是怎么算的呢?其实手机号为13552754279,而91代表短消息中心地址的类型(81&h表示国内,91&h表示国际)。所以0d表示的就是后面683155724572f9的长度(f9中的f是用来凑偶数位的)。
00 PID 协议标识,通常设为00就好。
0b DCS 数据编码方案,含有中文字符的话一般默认都为0b,即00001011转化而来,具体每一位的含义暂时不做详细解释。
11117091914323 时间戳,代表的时间为11年11月07日19时19分34秒,后面的23表示时区(什么这么表示尚在研究中,可以写死,感觉意义不大)。
0a4e0b73ed4e864e48ff1f 短信的具体内容,其中0a表示信息的长度。
好了,关于pdu我们分析完了,现在要做的就是如何获取这个pdu并传进copyMessageToIcc方法中。观察源码会发现在framework/base/telephony/java/com/android/internal/telephony/gsm下的SmsMessage类中有一个getSubmitPdu方法,返回一个SubmitPdu对象,该对象有encodedScAddress和encodedMessage两个byte[]数组类型的属性,而且在多个地方被用到,和copyMessageToIcc中的参数十分相似,会不会就是我们要找的呢?经调用后发现,得到的pdu并不正确,但是没有关系,我们可以将其涉及到的方法重写,拼出我们想要的pdu!我改写的代码如下:
private SubmitPdu getSubmitPdu(String scAddress, String destinationAddress, String message,
boolean statusReportRequested, byte[] header, int encoding, long date) {
// Perform null parameter checks.
if (message == null || destinationAddress == null) {
return null;
}
SubmitPdu ret = new SubmitPdu();
// MTI = SMS-SUBMIT, UDHI = header != null
//在这个方法中获得了数据编码方案(DCS,就是例子中的0b)前的所有字节。
ByteArrayOutputStream bo = getSubmitPduHead(scAddress, destinationAddress,
statusReportRequested, ret);
// User Data (and length)
byte[] userData;
if (encoding == ENCODING_UNKNOWN) {
// First, try encoding it with the GSM alphabet
encoding = ENCODING_7BIT;
}
try {
if (encoding == ENCODING_7BIT) {
userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header);
} else { // assume UCS-2
try {
userData = encodeUCS2(message, header);
} catch (UnsupportedEncodingException uex) {
Log.e("GSM", "Implausible UnsupportedEncodingException ", uex);
return null;
}
}
} catch (EncodeException ex) {
// Encoding to the 7-bit alphabet failed. Let's see if we can
// send it as a UCS-2 encoded message
try {
userData = encodeUCS2(message, header);
encoding = ENCODING_16BIT;
} catch (UnsupportedEncodingException uex) {
Log.e("GSM", "Implausible UnsupportedEncodingException ", uex);
return null;
}
}
if (encoding == ENCODING_7BIT) {
if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
// Message too long
return null;
}
bo.write(0x00);
} else { // assume UCS-2
if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
// Message too long
return null;
}
// TP-Data-Coding-Scheme
// Class 3, UCS-2 encoding, uncompressed
bo.write(0x0b); //DCS,数据编码方案
}
//获得所需的时间戳,否则会显示乱码
byte[] timeStamp = getTimeStamp(date);
for (int i=0; i<timeStamp.length; i++){
bo.write(timeStamp[i]);
}
bo.write(0x23);
//写入短信信息,含有中文时会自动捕获异常并改变其编码格式
bo.write(userData, 0, userData.length);
ret.encodedMessage = bo.toByteArray();
return ret;
}
private static ByteArrayOutputStream getSubmitPduHead(
String scAddress, String destinationAddress,
boolean statusReportRequested, SubmitPdu ret) {
ByteArrayOutputStream bo = new ByteArrayOutputStream(
MAX_USER_DATA_BYTES + 40);
// SMSC address with length octet, or 0
if (scAddress == null) {
ret.encodedScAddress = null;
} else {
ret.encodedScAddress = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
scAddress);
}
// TP-Message-Type-Indicator (and friends)
if (statusReportRequested) {
// Set TP-Status-Report-Request bit.
mtiByte |= 0x20;
if (Config.LOGD) Log.d("GSM", "SMS status report requested");
}
//bo.write(0);
//bo.write(mtiByte);
if(null != ret.encodedScAddress) {
for (int i=0,len=ret.encodedScAddress.length;i<len;i++){
bo.write(ret.encodedScAddress[i]);
}
}
// space for TP-Message-Reference
bo.write(0);
byte[] daBytes;
daBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(destinationAddress);
// destination address length in BCD digits, ignoring TON byte and pad
// TODO Should be better.
bo.write((daBytes.length - 1) * 2
- ((daBytes[daBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));
// destination address
bo.write(daBytes, 0, daBytes.length);
// TP-Protocol-Identifier
bo.write(0);
return bo;
}
private byte[] encodeUCS2(String message, byte[] header)
throws UnsupportedEncodingException {
byte[] userData, textPart;
textPart = message.getBytes("utf-16be");
if (header != null) {
// Need 1 byte for UDHL
userData = new byte[header.length + textPart.length + 1];
userData[0] = (byte) header.length;
System.arraycopy(header, 0, userData, 1, header.length);
System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
} else {
userData = textPart;
}
byte[] ret = new byte[userData.length + 1];
ret[0] = (byte) (userData.length & 0xff);
System.arraycopy(userData, 0, ret, 1, userData.length);
return ret;
}
private byte[] getTimeStamp(long time){
byte[] timeStamp=null;
StringBuilder builder = new StringBuilder();
SimpleDateFormat sdf=new SimpleDateFormat("yy-MM-dd-hh-mm-ss");
String[] array = sdf.format(time).split("-");
for(int i=0;i<array.length;i++){
int p = Integer.parseInt(array[i]);
int q = p/10 + 10*(p%10);
builder = q<10 ? builder.append("0"+q) : builder.append(q);
}
timeStamp = IccUtils.hexStringToBytes(builder.toString());
return timeStamp;
}
调用上述方法后,就会得到我们想要的pdu了,但是会发现还是无法成功地将信息写入SIM卡上,究竟是为什么呢!分析log后才知道,原来pdu还少了一些东西!在将pdu传给底层的modem时,还要在其前面添加“00”,表示点对点发送,这就要更改hardware/ril/reference-ril/reference-sc8800s.c文件里的requestWriteSmsToSim了,具体代码如下:
RIL_SMS_WriteArgs *p_args;
char *cmd;
char *pdu;
int length;
int err;
ATResponse *p_response = NULL;
length = strlen(p_args->pdu)/2;
asprintf(&cmd, "AT+CMGW=%d,%d", length, p_args->status);
asprintf(&pdu, "00%s", p_args->pdu);
err = at_send_command_sms(cmd, pdu, "+CMGW:", &p_response);
free(cmd); // add by lhhuang@ingenic.cn
free(pdu);
编译so后push到手机中,重启一下就大功告成了!
SmsManager smsManager = SmsManager.getDefault(); //用来获取一个SmsManager对象
现在我们来看一下copyMessageToIcc(byte[] smsc, byte[] pdu, int status)这个方法的三个参数:
1) byte[] smsc : 短信服务中心的地址,个人认为在复制到SIM卡过程中可以为空。
2) byte[] pdu : 中文翻译是协议数据单元,这个参数最为重要,一会我们会做详细地解释说明。
3) int status : 短信存储在Icc卡上的状态,有4种状态,1是已读,3是未读,5是已发送,7是未发送。
其实要想将将短信正确地存储到SIM卡上,pdu这个参数尤为重要,下面我们就来分析一下!
首先先看一下复制短信到SIM卡时log显示的正确pdu:
00 00 0d91683155724572f9 00 0b 11117091914323 0a4e0b73ed4e864e48ff1f
原本上面的pdu是完整连续,为了方便解释说明,特意加了空格区分开来。
00 SC Address 短信服务中心地址,通常我们发送短信时会发送一个pdu到短信服务中心,然后短信服务中心会对pdu进行一些处理再发送到目的手机,这其中就包括增加了这个SC Address和后面会介绍的时间戳。
00 PDUType pdu的第一个八位位组,即一个八位的二进制数转变成十六进制而来,每一位代表什么意思呢?由高到低依次代表RP(应答路径)、UDHI(用户数据头标识)、SRR(状态请求报告)、VPF(有效期格式,2位)、RD(拒绝副本)、MTI(信息指示类型,2位),在这里我们其实只要全部将其设置为0就好,于是便显示为00。
0d91683155724572f9 这一段代表了目的手机的号码,0d代表后面的地址长度(二进制下为13),那么这十三个数是怎么算的呢?其实手机号为13552754279,而91代表短消息中心地址的类型(81&h表示国内,91&h表示国际)。所以0d表示的就是后面683155724572f9的长度(f9中的f是用来凑偶数位的)。
00 PID 协议标识,通常设为00就好。
0b DCS 数据编码方案,含有中文字符的话一般默认都为0b,即00001011转化而来,具体每一位的含义暂时不做详细解释。
11117091914323 时间戳,代表的时间为11年11月07日19时19分34秒,后面的23表示时区(什么这么表示尚在研究中,可以写死,感觉意义不大)。
0a4e0b73ed4e864e48ff1f 短信的具体内容,其中0a表示信息的长度。
好了,关于pdu我们分析完了,现在要做的就是如何获取这个pdu并传进copyMessageToIcc方法中。观察源码会发现在framework/base/telephony/java/com/android/internal/telephony/gsm下的SmsMessage类中有一个getSubmitPdu方法,返回一个SubmitPdu对象,该对象有encodedScAddress和encodedMessage两个byte[]数组类型的属性,而且在多个地方被用到,和copyMessageToIcc中的参数十分相似,会不会就是我们要找的呢?经调用后发现,得到的pdu并不正确,但是没有关系,我们可以将其涉及到的方法重写,拼出我们想要的pdu!我改写的代码如下:
private SubmitPdu getSubmitPdu(String scAddress, String destinationAddress, String message,
boolean statusReportRequested, byte[] header, int encoding, long date) {
// Perform null parameter checks.
if (message == null || destinationAddress == null) {
return null;
}
SubmitPdu ret = new SubmitPdu();
// MTI = SMS-SUBMIT, UDHI = header != null
//在这个方法中获得了数据编码方案(DCS,就是例子中的0b)前的所有字节。
ByteArrayOutputStream bo = getSubmitPduHead(scAddress, destinationAddress,
statusReportRequested, ret);
// User Data (and length)
byte[] userData;
if (encoding == ENCODING_UNKNOWN) {
// First, try encoding it with the GSM alphabet
encoding = ENCODING_7BIT;
}
try {
if (encoding == ENCODING_7BIT) {
userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header);
} else { // assume UCS-2
try {
userData = encodeUCS2(message, header);
} catch (UnsupportedEncodingException uex) {
Log.e("GSM", "Implausible UnsupportedEncodingException ", uex);
return null;
}
}
} catch (EncodeException ex) {
// Encoding to the 7-bit alphabet failed. Let's see if we can
// send it as a UCS-2 encoded message
try {
userData = encodeUCS2(message, header);
encoding = ENCODING_16BIT;
} catch (UnsupportedEncodingException uex) {
Log.e("GSM", "Implausible UnsupportedEncodingException ", uex);
return null;
}
}
if (encoding == ENCODING_7BIT) {
if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
// Message too long
return null;
}
bo.write(0x00);
} else { // assume UCS-2
if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
// Message too long
return null;
}
// TP-Data-Coding-Scheme
// Class 3, UCS-2 encoding, uncompressed
bo.write(0x0b); //DCS,数据编码方案
}
//获得所需的时间戳,否则会显示乱码
byte[] timeStamp = getTimeStamp(date);
for (int i=0; i<timeStamp.length; i++){
bo.write(timeStamp[i]);
}
bo.write(0x23);
//写入短信信息,含有中文时会自动捕获异常并改变其编码格式
bo.write(userData, 0, userData.length);
ret.encodedMessage = bo.toByteArray();
return ret;
}
private static ByteArrayOutputStream getSubmitPduHead(
String scAddress, String destinationAddress,
boolean statusReportRequested, SubmitPdu ret) {
ByteArrayOutputStream bo = new ByteArrayOutputStream(
MAX_USER_DATA_BYTES + 40);
// SMSC address with length octet, or 0
if (scAddress == null) {
ret.encodedScAddress = null;
} else {
ret.encodedScAddress = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
scAddress);
}
// TP-Message-Type-Indicator (and friends)
if (statusReportRequested) {
// Set TP-Status-Report-Request bit.
mtiByte |= 0x20;
if (Config.LOGD) Log.d("GSM", "SMS status report requested");
}
//bo.write(0);
//bo.write(mtiByte);
if(null != ret.encodedScAddress) {
for (int i=0,len=ret.encodedScAddress.length;i<len;i++){
bo.write(ret.encodedScAddress[i]);
}
}
// space for TP-Message-Reference
bo.write(0);
byte[] daBytes;
daBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(destinationAddress);
// destination address length in BCD digits, ignoring TON byte and pad
// TODO Should be better.
bo.write((daBytes.length - 1) * 2
- ((daBytes[daBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));
// destination address
bo.write(daBytes, 0, daBytes.length);
// TP-Protocol-Identifier
bo.write(0);
return bo;
}
private byte[] encodeUCS2(String message, byte[] header)
throws UnsupportedEncodingException {
byte[] userData, textPart;
textPart = message.getBytes("utf-16be");
if (header != null) {
// Need 1 byte for UDHL
userData = new byte[header.length + textPart.length + 1];
userData[0] = (byte) header.length;
System.arraycopy(header, 0, userData, 1, header.length);
System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
} else {
userData = textPart;
}
byte[] ret = new byte[userData.length + 1];
ret[0] = (byte) (userData.length & 0xff);
System.arraycopy(userData, 0, ret, 1, userData.length);
return ret;
}
private byte[] getTimeStamp(long time){
byte[] timeStamp=null;
StringBuilder builder = new StringBuilder();
SimpleDateFormat sdf=new SimpleDateFormat("yy-MM-dd-hh-mm-ss");
String[] array = sdf.format(time).split("-");
for(int i=0;i<array.length;i++){
int p = Integer.parseInt(array[i]);
int q = p/10 + 10*(p%10);
builder = q<10 ? builder.append("0"+q) : builder.append(q);
}
timeStamp = IccUtils.hexStringToBytes(builder.toString());
return timeStamp;
}
调用上述方法后,就会得到我们想要的pdu了,但是会发现还是无法成功地将信息写入SIM卡上,究竟是为什么呢!分析log后才知道,原来pdu还少了一些东西!在将pdu传给底层的modem时,还要在其前面添加“00”,表示点对点发送,这就要更改hardware/ril/reference-ril/reference-sc8800s.c文件里的requestWriteSmsToSim了,具体代码如下:
RIL_SMS_WriteArgs *p_args;
char *cmd;
char *pdu;
int length;
int err;
ATResponse *p_response = NULL;
length = strlen(p_args->pdu)/2;
asprintf(&cmd, "AT+CMGW=%d,%d", length, p_args->status);
asprintf(&pdu, "00%s", p_args->pdu);
err = at_send_command_sms(cmd, pdu, "+CMGW:", &p_response);
free(cmd); // add by lhhuang@ingenic.cn
free(pdu);
编译so后push到手机中,重启一下就大功告成了!
相关推荐
"android写sim卡短信"这个话题涉及到的是如何通过编程方式来操作SIM卡中的联系人和短信,包括增加、删除、修改和查询这些数据。在Android中,这通常需要使用到一些隐藏API,因为标准的Android SDK并不直接支持对SIM...
在Android系统中,操作SIM卡涉及到的是与移动通信相关的功能,包括访问SIM卡中的联系人(电话本)和短信。Android提供了相应的API接口,允许开发者在权限允许的情况下进行这些操作。下面将详细介绍如何在Android中...
采用反射来调用android 隐藏的API 实现对sim卡中的联系人和短信进行增删改查。 SIM卡管家主要功能如下:1、sim卡上的短信导出 2、sim卡联系人导出 3、sim卡上的短信导出 4、sim卡上的联系人导出 5、可以查看sim卡上...
我们可以调用`getSimMessages()`方法来获取SIM卡中的短信,但需要注意的是,这个方法在某些设备或API版本上可能不可用,因为出于隐私和安全考虑,Android系统对SIM卡短信的访问进行了限制。 在Android中,短信通常...
在Android开发中,获取联系人、SIM卡信息以及最近联系人列表是常见的需求。下面将详细解释这些概念和实现方式。 一、Android联系人管理 Android系统提供了ContentProvider接口来访问和操作联系人数据。主要涉及到...
在Android系统中,SIM卡中的短信中心号码(SMSC,Short Message Service Center)是用于发送和接收短信的重要配置。有时,Android设备可能会遇到无法自动识别或更新SMSC的情况,导致短信功能异常,如发送失败、SMSC...
SIM卡应用下载主要涉及到的是移动通信领域中的一种特殊应用,主要与手机的SIM(Subscriber Identity Module,用户身份模块)或UIM(Universal Integrated Circuit Card,全球通用集成电路卡)卡相关。在描述中提到的...
在Android中,可以通过`ContentResolver`和特定的`Uri`来查询SIM卡中的联系人。`ContactsContract.SimContact`提供了访问SIM卡联系人的接口。例如,你可以使用以下代码来获取SIM卡的所有联系人: ```java ...
- 在Android 10(API level 29)及以上版本,由于隐私政策的改变,第三方应用可能无法访问某些短信,比如SIM卡上的短信。 - 监听短信可能会因系统或第三方安全应用的限制而无法正常工作,例如在某些设备上,系统默认...
在Android 4.X系统中,开发人员经常需要访问和操作SIM卡中的短信和联系人信息。本文将深入探讨两个关键的Android类:`IccSmsInterfaceManager`和`IccPhoneBookInterfaceManagerProxy`,它们是实现这些功能的核心。 ...
首先,Android短信服务主要基于TelephonyManager、SmsManager和ContentProvider等组件。`TelephonyManager`是获取手机通话状态和信息的主要接口,包括获取SIM卡信息、网络状态等。`SmsManager`则负责短信的发送和...
在这个"Android高级应用源码-SIM卡运营商(获取手机号码功能暂时无法实现).zip"中,我们主要探讨的是如何获取SIM卡信息,特别是运营商名称,尽管获取手机号码的功能尚未实现。这个项目可能是一个针对开发者学习和...
这个“Android例子源码GPS、GPRS、WIFI状态判断+SIM卡信息读取”示例提供了相关的代码实现,帮助开发者了解如何在Android应用中检查这些关键信息。 首先,我们来探讨GPS(全球定位系统)状态的判断。在Android中,...
在Android开发中,发送短信是一个常见的功能需求。开发者可以通过调用`SmsManager`类来实现发送短信的功能。 ### 1.1 发送短信的关键步骤: 1. **获取SmsManager实例**:通过`SmsManager.getDefault()`方法来获取...
Android 系统中,MIUI 通知类短信权限是一个特殊的权限,它不同于一般的短信权限。MIUI 的设计让开发者感到困惑,因为它不遵循 Android 的标准动态权限申请流程。MIUI 的 READ_SMS 权限申请不弹出提示框,让用户不...
本实战应用主要涵盖了如何通过Android源码获取手机通讯录,包括SIM卡中的联系人信息。接下来,我们将详细讨论这个主题。 首先,我们要知道在Android中访问通讯录主要涉及`ContactsContract`类,它是Android系统提供...
- 如果短信数据库为空,或者设备未插入SIM卡,可能无法获取到短信中心号码。 - 获取短信中心号码可能会涉及用户隐私,因此在实际应用中应确保有明确的用户授权和隐私策略。 以上就是Android获取短信中心号码的...
在这个类中,系统会将短信记录存储到 SIM 卡中,并提供短信记录的管理功能。 Android 短信SMS发送代码流程是一个复杂的过程,涉及到多个层次和多个类的协作。只有通过深入了解 Android 短信SMS发送代码流程,才能更...
在Android平台上,获取手机电话号码和短信内容是两个不同的任务,涉及到系统权限和特定API的使用。下面将分别详细介绍这两个知识点。 ### 手机电话号码的获取 在Android中,获取设备的电话号码通常需要使用`...
为了确保用户体验,还需考虑错误处理,例如检查是否有可用的SIM卡、短信服务是否开启等。此外,根据不同的Android版本和设备,可能需要适配短信彩信服务(MMS/SMS)的新特性或限制。 在"duan"这个文件中,可能包含...