2013-10-30 97 views
2

我开发了一个应用程序发送短信,使用BroadcastReceivers发送和(不)发送消息成功。Android:获取短信发送时间

在我的收货接收器中,我希望获得时间,当邮件发送给目标收件人时。由于发送和接收设备偶尔会被关闭,所以我认为,将收到广播的时间视为真正的交付时间是不正确的。

有没有办法在我的广播接收机中获得正确的传送时间? 谢谢。

回答

0

没有尝试过,但你可以看到,如果该信息传递给广播接收器

@Override 
public void onReceive(Context context, Intent intent) { 

    Bundle bundle=intent.getExtras(); 

    Object[] messages=(Object[])bundle.get("pdus"); 
    SmsMessage[] sms=new SmsMessage[messages.length]; 

    for(int n=0;n<messages.length;n++){ 
     sms[n]=SmsMessage.createFromPdu((byte[]) messages[n]); 
    } 
} 

意图的额外“的PDU”也许sms[0].getTimestampMillis()是你在寻找什么。

+0

非常感谢您的回复,我意识到,它看起来类似于传入的短信广播接收器。很快我会在真实设备上测试它,并且会看到getTimestampMillis()携带的信息。我希望,我会立即接受你的回答。 :-) –

+0

getTimestampMillis()返回以毫秒为单位的纪元时间(类似于unix时间)。您可以使用以下来获得一个日期'新日期(短信[0] .getTimestampMillis()* 1000)' –

+0

我认为是'日期日期=新日期(短信[0] .getTimestampMillis());'无* 1000 。可以阅读[这里](http://developer.android.com/reference/java/util/Date.html#Date%28long%29) – ramaral

1

这是一个工作解决方案。

通过研究com.android.internal.telephony.gsm.SmsMessage类(这是Android的内部类与解析上GSM/3GPP网络SMS的PDU涉及)的源代码,我发现,SMS-STATUS-REPORTS(=“递送报告”)包含第二时间戳值,即“放电时间”。这个值,不幸的是没有公开SmsMessage类,这是你想要的交付时间(由交付网络基础设施所感知)。

使用下面的类可以通过调用getDischargeTime()来获得该值。

getServiceCenterTimeStamp()方法返回从SmsMessage#getTimestampMillis()获得的相同值,即SMS服务中心收到原始消息的时间。

import android.telephony.PhoneNumberUtils; import 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, &lt;pdu&gt; 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); 
     } 

    } 

} 

一些免责声明:

  • 这仅适用于GSM/3GPP网络,而不是CDMA/3GPP;
  • 因为它是基于Android源代码的,上面的代码是Apache License v2.0许可的。