- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我开发了一个用于发送 SMS 消息的应用程序,使用 BroadcastReceivers 成功发送和(未)传递消息。
在我的投递接收者中,我想知道消息被投递给目标收件人的时间。由于偶尔会关闭发送和接收设备,我认为将我收到传送广播的时间视为实际传送时间是不正确的。
有没有办法在我的广播接收器中获得正确的传送时间?谢谢。
最佳答案
这是一个可行的解决方案。
通过研究com.android.internal.telephony.gsm.SmsMessage的源代码类(这是处理在 GSM/3GPP 网络上解析 SMS PDU 的 Android 内部类)我发现 SMS-STATUS-REPORTS
(=“发送报告”)包含第二个时间戳值,即“放电时间”。不幸的是,公共(public) SmsMessage
类没有公开该值,它是您想要的传递时间(由传递网络基础设施感知)。
使用下面的类,您可以通过调用 getDischargeTime()
获取此值。
getServiceCenterTimeStamp()
方法返回的值与您从 SmsMessage#getTimestampMillis()
获得的值相同,即 SMS 服务中心收到原始消息的时间。
导入android.telephony.PhoneNumberUtils; 导入 android.text.format.Time;
/**
* A helper class to parse (from pdu byte[]) and represent a GSM SMS-STATUS-REPORT message (= delivery report).
*
* Only works for GSM/3GPP networks (not CDMA/3GPP2).
*
* Based on the source code of the following Android classes:
* - com.android.internal.telephony.gsm.SmsMessage (almost everything)
* - com.android.internal.telephony.uicc.IccUtils (1 method)
* All licensed under the Apache License, Version 2.0.
* The code is taken from Android v5.1.0_r1 (+ 1 line from v4.2_r1).
*
* @author mstevens
*/
public class SMSStatusReport
{
static final String LOG_TAG = SMSStatusReport.class.getSimpleName();
/**
* TP-Message-Type-Indicator
* 9.2.3
*/
private int mMti;
/**
* TP-Status - status of a previously submitted SMS.
* This field applies to SMS-STATUS-REPORT messages. 0 indicates success;
* see TS 23.040, 9.2.3.15 for description of other possible values.
*/
private int mStatus;
/**
* TP-Status - status of a previously submitted SMS.
* This field is true iff the message is a SMS-STATUS-REPORT message.
*/
private boolean mIsStatusReportMessage = false;
/**
* TP-Service-Centre-Time-Stamp
*/
private long serviceCenterTimeStamp;
/**
* TP-Discharge-Time
*/
private long dischargeTime;
/**
* Constructor
*
* @param pdu
*/
public SMSStatusReport(byte[] pdu)
{
// Parse:
createFromPdu(pdu);
if(!mIsStatusReportMessage)
throw new IllegalArgumentException("This is not the pdu of a GSM SMS-STATUS-REPORT message");
}
/**
* TS 27.005 3.1, <pdu> definition "In the case of SMS: 3GPP TS 24.011 [6]
* SC address followed by 3GPP TS 23.040 [3] TPDU in hexadecimal format:
* ME/TA converts each octet of TP data unit into two IRA character long
* hex number (e.g. octet with integer value 42 is presented to TE as two
* characters 2A (IRA 50 and 65))" ...in the case of cell broadcast,
* something else...
*
* @param pdu
*
* @see Adapted from {@link com.android.internal.telephony.gsm.SmsMessage#createFromPdu(byte[])} (originally static)
*/
private void createFromPdu(byte[] pdu)
{
PduParser p = new PduParser(pdu);
/*Object mScAddress = */p.getSCAddress();
// TP-Message-Type-Indicator
// 9.2.3
int firstByte = p.getByte();
mMti = firstByte & 0x3;
switch (mMti)
{
// TP-Message-Type-Indicator
// 9.2.3
case 0:
case 3: //GSM 03.40 9.2.3.1: MTI == 3 is Reserved.
//This should be processed in the same way as MTI == 0 (Deliver)
//parseSmsDeliver(p, firstByte);
break;
case 1:
//parseSmsSubmit(p, firstByte);
break;
case 2:
parseSmsStatusReport(p, firstByte);
break;
default:
throw new RuntimeException("Unsupported message type");
}
}
/**
* Parses a SMS-STATUS-REPORT message.
*
* @param p A PduParser, cued past the first byte.
* @param firstByte The first byte of the PDU, which contains MTI, etc.
*/
private void parseSmsStatusReport(PduParser p, int firstByte)
{
mIsStatusReportMessage = true;
// TP-Message-Reference
/*int mMessageRef = */p.getByte();
// TP-Recipient-Address
/*Object mRecipientAddress = */p.getAddress();
// TP-Service-Centre-Time-Stamp
serviceCenterTimeStamp = p.getSCTimestampMillis();
// TP-Discharge-Time (line taken from Android v4.2_r1)
dischargeTime = p.getSCTimestampMillis();
// TP-Status
mStatus = p.getByte();
// The following are optional fields that may or may not be present.
if (p.moreDataPresent())
{/*
// TP-Parameter-Indicator
int extraParams = p.getByte();
int moreExtraParams = extraParams;
while ((moreExtraParams & 0x80) != 0) {
// We only know how to parse a few extra parameters, all
// indicated in the first TP-PI octet, so skip over any
// additional TP-PI octets.
moreExtraParams = p.getByte();
}
// As per 3GPP 23.040 section 9.2.3.27 TP-Parameter-Indicator,
// only process the byte if the reserved bits (bits3 to 6) are zero.
if ((extraParams & 0x78) == 0) {
// TP-Protocol-Identifier
if ((extraParams & 0x01) != 0) {
mProtocolIdentifier = p.getByte();
}
// TP-Data-Coding-Scheme
if ((extraParams & 0x02) != 0) {
mDataCodingScheme = p.getByte();
}
// TP-User-Data-Length (implies existence of TP-User-Data)
if ((extraParams & 0x04) != 0) {
boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
parseUserData(p, hasUserDataHeader);
}
}*/
}
}
/**
* @return whether or not the original message was received on the receiver handset
*/
public boolean isReceived()
{
return mStatus == 0;
}
/**
* @return the serviceCenterTimeStamp
*/
public long getServiceCenterTimeStamp()
{
return serviceCenterTimeStamp;
}
/**
* @return the dischargeTime
*/
public long getDischargeTime()
{
return dischargeTime;
}
private static class PduParser
{
byte mPdu[];
int mCur;
PduParser(byte[] pdu)
{
mPdu = pdu;
mCur = 0;
}
/**
* Parse and return the SC address prepended to SMS messages coming via the TS 27.005 / AT interface.
* Returns null on invalid address
*/
String getSCAddress()
{
int len;
String ret;
// length of SC Address
len = getByte();
if(len == 0)
{
// no SC address
ret = null;
}
else
{
// SC address
try
{
ret = PhoneNumberUtils.calledPartyBCDToString(mPdu, mCur, len);
}
catch(RuntimeException tr)
{
ret = null;
}
}
mCur += len;
return ret;
}
/**
* returns non-sign-extended byte value
*/
int getByte()
{
return mPdu[mCur++] & 0xff;
}
/**
* Any address except the SC address (eg, originating address)
* See TS 23.040 9.1.2.5
*
* mstevens: Made NON-FUNCTIONAL to remove dependency on internal Android classes. Always returns null but skips right number of bytes.
*/
Object/*GsmSmsAddress*/ getAddress()
{
//GsmSmsAddress ret;
// "The Address-Length field is an integer representation of
// the number field, i.e. excludes any semi-octet containing only
// fill bits."
// The TOA field is not included as part of this
int addressLength = mPdu[mCur] & 0xff;
int lengthBytes = 2 + (addressLength + 1) / 2;
/*try {
ret = new GsmSmsAddress(mPdu, mCur, lengthBytes);
} catch (ParseException e) {
ret = null;
//This is caught by createFromPdu(byte[] pdu)
throw new RuntimeException(e.getMessage());
}*/
mCur += lengthBytes;
return null;//ret;
}
/**
* Parses an SC timestamp and returns a currentTimeMillis()-style timestamp
*
* @see http://en.wikipedia.org/wiki/GSM_03.40#Time_Format
*/
long getSCTimestampMillis() {
// TP-Service-Centre-Time-Stamp
int year = gsmBcdByteToInt(mPdu[mCur++]);
int month = gsmBcdByteToInt(mPdu[mCur++]);
int day = gsmBcdByteToInt(mPdu[mCur++]);
int hour = gsmBcdByteToInt(mPdu[mCur++]);
int minute = gsmBcdByteToInt(mPdu[mCur++]);
int second = gsmBcdByteToInt(mPdu[mCur++]);
// For the timezone, the most significant bit of the
// least significant nibble is the sign byte
// (meaning the max range of this field is 79 quarter-hours,
// which is more than enough)
byte tzByte = mPdu[mCur++];
// Mask out sign bit.
int timezoneOffset = gsmBcdByteToInt((byte) (tzByte & (~0x08)));
timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset;
Time time = new Time(Time.TIMEZONE_UTC);
// It's 2006. Should I really support years < 2000?
time.year = year >= 90 ? year + 1900 : year + 2000;
time.month = month - 1;
time.monthDay = day;
time.hour = hour;
time.minute = minute;
time.second = second;
// Timezone offset is in quarter hours.
return time.toMillis(true) - (timezoneOffset * 15 * 60 * 1000);
}
/**
* Decodes a GSM-style BCD byte, returning an int ranging from 0-99.
*
* In GSM land, the least significant BCD digit is stored in the most
* significant nibble.
*
* Out-of-range digits are treated as 0 for the sake of the time stamp,
* because of this:
*
* TS 23.040 section 9.2.3.11
* "if the MS receives a non-integer value in the SCTS, it shall
* assume the digit is set to 0 but shall store the entire field
* exactly as received"
*
* @see Taken from com.android.internal.telephony.uicc.IccUtils
*/
public static int gsmBcdByteToInt(byte b)
{
int ret = 0;
// treat out-of-range BCD values as 0
if((b & 0xf0) <= 0x90)
ret = (b >> 4) & 0xf;
if((b & 0x0f) <= 0x09)
ret += (b & 0xf) * 10;
return ret;
}
public boolean moreDataPresent()
{
return (mPdu.length > mCur);
}
}
}
一些免责声明:
关于Android:获取短信发送时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19681290/
我最近在/ drawable中添加了一些.gifs,以便可以将它们与按钮一起使用。这个工作正常(没有错误)。现在,当我重建/运行我的应用程序时,出现以下错误: Error: Gradle: Execu
Android 中有返回内部存储数据路径的方法吗? 我有 2 部 Android 智能手机(Samsung s2 和 s7 edge),我在其中安装了一个应用程序。我想使用位于这条路径中的 sqlit
这个问题在这里已经有了答案: What's the difference between "?android:" and "@android:" in an android layout xml f
我只想知道 android 开发手机、android 普通手机和 android root 手机之间的实际区别。 我们不能从实体店或除 android marketplace 以外的其他地方购买开发手
自Gradle更新以来,我正在努力使这个项目达到标准。这是一个团队项目,它使用的是android-apt插件。我已经进行了必要的语法更改(编译->实现和apt->注释处理器),但是编译器仍在告诉我存在
我是android和kotlin的新手,所以请原谅要解决的一个非常简单的问题! 我已经使用导航体系结构组件创建了一个基本应用程序,使用了底部的导航栏和三个导航选项。每个导航选项都指向一个专用片段,该片
我目前正在使用 Facebook official SDK for Android . 我现在正在使用高级示例应用程序,但我不知道如何让它获取应用程序墙/流/状态而不是登录的用户。 这可能吗?在那种情
我在下载文件时遇到问题, 我可以在模拟器中下载文件,但无法在手机上使用。我已经定义了上网和写入 SD 卡的权限。 我在服务器上有一个 doc 文件,如果用户单击下载。它下载文件。这在模拟器中工作正常但
这个问题在这里已经有了答案: What is the difference between gravity and layout_gravity in Android? (22 个答案) 关闭 9
任何人都可以告诉我什么是 android 缓存和应用程序缓存,因为当我们谈论缓存清理应用程序时,它的作用是,缓存清理概念是清理应用程序缓存还是像内存管理一样主存储、RAM、缓存是不同的并且据我所知,缓
假设应用程序 Foo 和 Eggs 在同一台 Android 设备上。任一应用程序都可以获取设备上所有应用程序的列表。一个应用程序是否有可能知道另一个应用程序是否已经运行以及运行了多长时间? 最佳答案
我有点困惑,我只看到了从 android 到 pc 或者从 android 到 pc 的例子。我需要制作一个从两部手机 (android) 连接的 android 应用程序进行视频聊天。我在想,我知道
用于使用 Android 以编程方式锁定屏幕。我从 Stackoverflow 之前关于此的问题中得到了一些好主意,并且我做得很好,但是当我运行该代码时,没有异常和错误。而且,屏幕没有锁定。请在这段代
文档说: android:layout_alignParentStart If true, makes the start edge of this view match the start edge
我不知道这两个属性和高度之间的区别。 以一个TextView为例,如果我将它的layout_width设置为wrap_content,并将它的width设置为50 dip,会发生什么情况? 最佳答案
这两个属性有什么关系?如果我有 android:noHistory="true",那么有 android:finishOnTaskLaunch="true" 有什么意义吗? 最佳答案 假设您的应用中有
我是新手,正在尝试理解以下 XML 代码: 查看 developer.android.com 上的文档,它说“starStyle”是 R.attr 中的常量, public static final
在下面的代码中,为什么当我设置时单选按钮的外观会发生变化 android:layout_width="fill_parent" 和 android:width="fill_parent" 我说的是
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 9
假设我有一个函数 fun myFunction(name:String, email:String){},当我调用这个函数时 myFunction('Ali', 'ali@test.com ') 如何
我是一名优秀的程序员,十分优秀!