2013-10-30 4 views
2

SMS 메시지를 보내고 전달 된 메시지와 전달하지 않은 메시지에 대해 BroadcastReceivers를 사용하는 응용 프로그램을 개발했습니다.Android : SMS 배달 시간을 받으십시오.

수신자가 수신자에게 메시지가 배달 된 시간을 받고 싶습니다. 때로는 송수신 장치를 모두 끌 수 있기 때문에 실제 배달 시간으로 배달 브로드 캐스트를받는 시간을 고려하는 것은 올바르지 않다고 가정합니다.

내 브로드 캐스트 수신기에서 올바른 배달 시간을 얻는 방법이 있습니까? 감사합니다.

답변

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

답장을 보내 주셔서 감사합니다. 들어온 SMS 수신자와 비슷합니다. 곧 나는 실제 장치에서이를 테스트 할 것이고 getTimestampMillis()가 전달하는 정보를 볼 것입니다. 나는 당신의 대답을 바로 받아 들일 수 있기를 바랍니다. :-) –

+0

getTimestampMillis()는 밀리 초 단위의 에포크 시간을 반환합니다 (유닉스 시간과 유사). 다음 날짜를 얻기 위해 다음을 사용할 수 있습니다 :'new date (sms [0] .getTimestampMillis() * 1000)' –

+0

'Date date = new Date (sms [0] .getTimestampMillis()); . 읽을 수있는 [여기] (http://developer.android.com/reference/java/util/Date.html#Date%28long%29) – ramaral

1

다음은 작동하는 해결책입니다.

클래스의 소스 코드 (GSM/3GPP 네트워크에서 SMS PDU를 구문 분석하는 Android의 내부 클래스)를 조사한 결과 SMS-STATUS-REPORTS (= "배달 보고서")에 두 번째 타임 스탬프 값, 즉 "방전 시각". 불행하게도 공개 SmsMessage 클래스에 의해 노출되지 않는이 값은 (전달하는 네트워크 인프라에 의해 감지되는) 원하는 전달 시간입니다.

아래 클래스를 사용하면 getDischargeTime()을 호출하여이 값을 얻을 수 있습니다.

메서드는 SmsMessage#getTimestampMillis()에서 가져온 것과 동일한 값, 즉 SMS 서비스 센터에서 원본 메시지를받은 시간을 반환합니다.

import 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, &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 버전 2.0 라이선스입니다.
관련 문제