diff options
6 files changed, 463 insertions, 139 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 327f64268f8f..a9f977ecbbb4 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -11756,6 +11756,7 @@ package android.telephony { } public final class SmsManager { + method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean copyMessageToIcc(@Nullable byte[], @NonNull byte[], int); method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean deleteMessageFromIcc(int); method public boolean disableCellBroadcastRange(int, int, int); method public boolean enableCellBroadcastRange(int, int, int); @@ -11773,6 +11774,7 @@ package android.telephony { public class SmsMessage { method @Nullable public static android.telephony.SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[], boolean); + method @Nullable public static android.telephony.SmsMessage.SubmitPdu getSmsPdu(int, int, @Nullable String, @NonNull String, @NonNull String, long); method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static byte[] getSubmitPduEncodedMessage(boolean, @NonNull String, @NonNull String, int, int, int, int, int, int); } diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index a88fb751d9d6..5cd7cf8fae8a 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -1658,7 +1658,7 @@ public final class SmsManager { } /** - * Copy a raw SMS PDU to the ICC. + * Copies a raw SMS PDU to the ICC. * ICC (Integrated Circuit Card) is the card of the device. * For example, this can be the SIM or USIM for GSM. * @@ -1672,21 +1672,26 @@ public final class SmsManager { * operation is performed on the correct subscription. * </p> * - * @param smsc the SMSC for this message, or NULL for the default SMSC - * @param pdu the raw PDU to store - * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD, - * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT) - * @return true for success + * @param smsc the SMSC for this messag or null for the default SMSC. + * @param pdu the raw PDU to store. + * @param status message status. One of these status: + * <code>STATUS_ON_ICC_READ</code> + * <code>STATUS_ON_ICC_UNREAD</code> + * <code>STATUS_ON_ICC_SENT</code> + * <code>STATUS_ON_ICC_UNSENT</code> + * @return true for success. Otherwise false. * - * @throws IllegalArgumentException if pdu is NULL - * {@hide} + * @throws IllegalArgumentException if pdu is null. + * @hide */ - @UnsupportedAppUsage - public boolean copyMessageToIcc(byte[] smsc, byte[] pdu,int status) { + @SystemApi + @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC) + public boolean copyMessageToIcc( + @Nullable byte[] smsc, @NonNull byte[] pdu, @StatusOnIcc int status) { boolean success = false; - if (null == pdu) { - throw new IllegalArgumentException("pdu is NULL"); + if (pdu == null) { + throw new IllegalArgumentException("pdu is null"); } try { ISms iSms = getISmsService(); @@ -2120,6 +2125,17 @@ public final class SmsManager { return ret; } + /** @hide */ + @IntDef(prefix = { "STATUS_ON_ICC_" }, value = { + STATUS_ON_ICC_FREE, + STATUS_ON_ICC_READ, + STATUS_ON_ICC_UNREAD, + STATUS_ON_ICC_SENT, + STATUS_ON_ICC_UNSENT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StatusOnIcc {} + // see SmsMessage.getStatusOnIcc /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index 703bd08d9391..7a30f143a3a4 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -585,13 +585,15 @@ public class SmsMessage { */ /** - * Get an SMS-SUBMIT PDU for a destination address and a message. + * Gets an SMS-SUBMIT PDU for a destination address and a message. * This method will not attempt to use any GSM national language 7 bit encodings. * - * @param scAddress Service Centre address. Null means use default. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is requested for this message. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. */ public static SubmitPdu getSubmitPdu(String scAddress, String destinationAddress, String message, boolean statusReportRequested) { @@ -604,17 +606,16 @@ public class SmsMessage { } /** - * Get an SMS-SUBMIT PDU for a destination address and a message. + * Gets an SMS-SUBMIT PDU for a destination address and a message. * This method will not attempt to use any GSM national language 7 bit encodings. * - * @param scAddress Service Centre address. Null means use default. + * @param scAddress Service Centre address. Null means use default. * @param destinationAddress the address of the destination for the message. - * @param message String representation of the message payload. - * @param statusReportRequested Indicates whether a report is requested for this message. - * @param subId Subscription of the message - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is requested for this message. + * @param subId subscription of the message. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. * @hide */ public static SubmitPdu getSubmitPdu(String scAddress, @@ -632,17 +633,16 @@ public class SmsMessage { } /** - * Get an SMS-SUBMIT PDU for a data message to a destination address & port. + * Gets an SMS-SUBMIT PDU for a data message to a destination address & port. * This method will not attempt to use any GSM national language 7 bit encodings. * - * @param scAddress Service Centre address. null == use default - * @param destinationAddress the address of the destination for the message - * @param destinationPort the port to deliver the message to at the - * destination - * @param data the data for the message - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param destinationPort the port to deliver the message to at the destination. + * @param data the data for the message. + * @param statusReportRequested indicates whether a report is requested for this message. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. */ public static SubmitPdu getSubmitPdu(String scAddress, String destinationAddress, short destinationPort, byte[] data, @@ -660,6 +660,55 @@ public class SmsMessage { return new SubmitPdu(spb); } + // TODO: SubmitPdu class is used for SMS-DELIVER also now. Refactor for SubmitPdu and new + // DeliverPdu accordingly. + + /** + * Gets an SMS PDU to store in the ICC. + * + * @param subId subscription of the message. + * @param status message status. One of these status: + * <code>SmsManager.STATUS_ON_ICC_READ</code> + * <code>SmsManager.STATUS_ON_ICC_UNREAD</code> + * <code>SmsManager.STATUS_ON_ICC_SENT</code> + * <code>SmsManager.STATUS_ON_ICC_UNSENT</code> + * @param scAddress Service Centre address. Null means use default. + * @param address destination or originating address. + * @param message string representation of the message payload. + * @param date the time stamp the message was received. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. + * @hide + */ + @SystemApi + @Nullable + public static SubmitPdu getSmsPdu(int subId, @SmsManager.StatusOnIcc int status, + @Nullable String scAddress, @NonNull String address, @NonNull String message, + long date) { + SubmitPduBase spb; + if (isCdmaVoice(subId)) { // 3GPP2 format + if (status == SmsManager.STATUS_ON_ICC_READ + || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU + spb = com.android.internal.telephony.cdma.SmsMessage.getDeliverPdu(address, + message, date); + } else { // Submit PDU + spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress, + address, message, false /* statusReportRequested */, null /* smsHeader */); + } + } else { // 3GPP format + if (status == SmsManager.STATUS_ON_ICC_READ + || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU + spb = com.android.internal.telephony.gsm.SmsMessage.getDeliverPdu(scAddress, + address, message, date); + } else { // Submit PDU + spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress, + address, message, false /* statusReportRequested */, null /* header */); + } + } + + return spb != null ? new SubmitPdu(spb) : null; + } + /** * Get an SMS-SUBMIT PDU's encoded message. * This is used by Bluetooth MAP profile to handle long non UTF-8 SMS messages. diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 7a374fa6ccd2..d0c8024c56fe 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -201,26 +201,16 @@ public class SmsMessage extends SmsMessageBase { } /** - * TODO(cleanup): why do getSubmitPdu methods take an scAddr input - * and do nothing with it? GSM allows us to specify a SC (eg, - * when responding to an SMS that explicitly requests the response - * is sent to a specific SC), or pass null to use the default - * value. Is there no similar notion in CDMA? Or do we just not - * have it hooked up? - */ - - /** - * Get an SMS-SUBMIT PDU for a destination address and a message + * Gets an SMS-SUBMIT PDU for a destination address and a message. * - * @param scAddr Service Centre address. Null means use default. - * @param destAddr Address of the recipient. - * @param message String representation of the message payload. - * @param statusReportRequested Indicates whether a report is requested for this message. - * @param smsHeader Array containing the data for the User Data Header, preceded - * by the Element Identifiers. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddr Service Centre address. No use for this message. + * @param destAddr the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is requested for this message. + * @param smsHeader array containing the data for the User Data Header, preceded by the Element + * Identifiers. + * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns + * null on encode error. * @hide */ @UnsupportedAppUsage @@ -230,18 +220,17 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a destination address and a message + * Gets an SMS-SUBMIT PDU for a destination address and a message. * - * @param scAddr Service Centre address. Null means use default. - * @param destAddr Address of the recipient. - * @param message String representation of the message payload. - * @param statusReportRequested Indicates whether a report is requested for this message. - * @param smsHeader Array containing the data for the User Data Header, preceded - * by the Element Identifiers. - * @param priority Priority level of the message - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddr Service Centre address. No use for this message. + * @param destAddr the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is requested for this message. + * @param smsHeader array containing the data for the User Data Header, preceded by the Element + * Identifiers. + * @param priority priority level of the message. + * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns + * null on encode error. * @hide */ @UnsupportedAppUsage @@ -264,16 +253,15 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a data message to a destination address and port. + * Gets an SMS-SUBMIT PDU for a data message to a destination address & port. * - * @param scAddr Service Centre address. null == use default - * @param destAddr the address of the destination for the message - * @param destPort the port to deliver the message to at the - * destination - * @param data the data for the message - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddr Service Centre address. No use for this message. + * @param destAddr the address of the destination for the message. + * @param destPort the port to deliver the message to at the destination. + * @param data the data for the message. + * @param statusReportRequested indicates whether a report is requested for this message. + * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns + * null on encode error. */ @UnsupportedAppUsage public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, int destPort, @@ -304,14 +292,13 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a data message to a destination address & port + * Gets an SMS-SUBMIT PDU for a data message to a destination address & port. * - * @param destAddr the address of the destination for the message - * @param userData the data for the message - * @param statusReportRequested Indicates whether a report is requested for this message. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param destAddr the address of the destination for the message. + * @param userData the data for the message. + * @param statusReportRequested indicates whether a report is requested for this message. + * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns + * null on encode error. */ @UnsupportedAppUsage public static SubmitPdu getSubmitPdu(String destAddr, UserData userData, @@ -320,15 +307,14 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a data message to a destination address & port + * Gets an SMS-SUBMIT PDU for a data message to a destination address & port. * - * @param destAddr the address of the destination for the message - * @param userData the data for the message - * @param statusReportRequested Indicates whether a report is requested for this message. - * @param priority Priority level of the message - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param destAddr the address of the destination for the message. + * @param userData the data for the message. + * @param statusReportRequested indicates whether a report is requested for this message. + * @param priority Priority level of the message. + * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns + * null on encode error. */ @UnsupportedAppUsage public static SubmitPdu getSubmitPdu(String destAddr, UserData userData, @@ -1058,6 +1044,72 @@ public class SmsMessage extends SmsMessageBase { } /** + * Gets an SMS-DELIVER PDU for a originating address and a message. + * + * @param origAddr the address of the originating for the message. + * @param message string representation of the message payload. + * @param date the time stamp the message was received. + * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns + * null on encode error. + * @hide + */ + public static SubmitPdu getDeliverPdu(String origAddr, String message, long date) { + if (origAddr == null || message == null) { + return null; + } + + CdmaSmsAddress addr = CdmaSmsAddress.parse(origAddr); + if (addr == null) return null; + + BearerData bearerData = new BearerData(); + bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER; + + bearerData.messageId = 0; + + bearerData.deliveryAckReq = false; + bearerData.userAckReq = false; + bearerData.readAckReq = false; + bearerData.reportReq = false; + + bearerData.userData = new UserData(); + bearerData.userData.payloadStr = message; + + bearerData.msgCenterTimeStamp = BearerData.TimeStamp.fromMillis(date); + + byte[] encodedBearerData = BearerData.encode(bearerData); + if (encodedBearerData == null) return null; + + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(100); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeInt(SmsEnvelope.TELESERVICE_WMT); + dos.writeInt(0); // servicePresent + dos.writeInt(0); // serviceCategory + dos.write(addr.digitMode); + dos.write(addr.numberMode); + dos.write(addr.ton); // number_type + dos.write(addr.numberPlan); + dos.write(addr.numberOfDigits); + dos.write(addr.origBytes, 0, addr.origBytes.length); // digits + // Subaddress is not supported. + dos.write(0); // subaddressType + dos.write(0); // subaddr_odd + dos.write(0); // subaddr_nbr_of_digits + dos.write(encodedBearerData.length); + dos.write(encodedBearerData, 0, encodedBearerData.length); + dos.close(); + + SubmitPdu pdu = new SubmitPdu(); + pdu.encodedMessage = baos.toByteArray(); + pdu.encodedScAddress = null; + return pdu; + } catch (IOException ex) { + Rlog.e(LOG_TAG, "creating Deliver PDU failed: " + ex); + } + return null; + } + + /** * Creates byte array (pseudo pdu) from SMS object. * Note: Do not call this method more than once per object! * @hide diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java index cbf0f5c297e1..6ad6dd119f50 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java @@ -32,6 +32,7 @@ import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.util.BitwiseInputStream; import com.android.internal.util.BitwiseOutputStream; +import java.io.ByteArrayOutputStream; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; @@ -284,6 +285,33 @@ public final class BearerData { return ts; } + public static TimeStamp fromMillis(long timeInMillis) { + TimeStamp ts = new TimeStamp(); + LocalDateTime localDateTime = + Instant.ofEpochMilli(timeInMillis).atZone(ts.mZoneId).toLocalDateTime(); + int year = localDateTime.getYear(); + if (year < 1996 || year > 2095) return null; + ts.year = year; + ts.month = localDateTime.getMonthValue(); + ts.monthDay = localDateTime.getDayOfMonth(); + ts.hour = localDateTime.getHour(); + ts.minute = localDateTime.getMinute(); + ts.second = localDateTime.getSecond(); + return ts; + } + + public byte[] toByteArray() { + int year = this.year % 100; // 00 - 99 + ByteArrayOutputStream outStream = new ByteArrayOutputStream(6); + outStream.write((((year / 10) & 0x0F) << 4) | ((year % 10) & 0x0F)); + outStream.write((((month / 10) << 4) & 0xF0) | ((month % 10) & 0x0F)); + outStream.write((((monthDay / 10) << 4) & 0xF0) | ((monthDay % 10) & 0x0F)); + outStream.write((((hour / 10) << 4) & 0xF0) | ((hour % 10) & 0x0F)); + outStream.write((((minute / 10) << 4) & 0xF0) | ((minute % 10) & 0x0F)); + outStream.write((((second / 10) << 4) & 0xF0) | ((second % 10) & 0x0F)); + return outStream.toByteArray(); + } + public long toMillis() { LocalDateTime localDateTime = LocalDateTime.of(year, month + 1, monthDay, hour, minute, second); @@ -957,6 +985,12 @@ public final class BearerData { } } + private static void encodeMsgCenterTimeStamp(BearerData bData, BitwiseOutputStream outStream) + throws BitwiseOutputStream.AccessException { + outStream.write(8, 6); + outStream.writeByteArray(8 * 6, bData.msgCenterTimeStamp.toByteArray()); + }; + /** * Create serialized representation for BearerData object. * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details) @@ -1021,6 +1055,10 @@ public final class BearerData { outStream.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS); encodeScpResults(bData, outStream); } + if (bData.msgCenterTimeStamp != null) { + outStream.write(8, SUBPARAM_MESSAGE_CENTER_TIME_STAMP); + encodeMsgCenterTimeStamp(bData, outStream); + } return outStream.toByteArray(); } catch (BitwiseOutputStream.AccessException ex) { Rlog.e(LOG_TAG, "BearerData encode failed: " + ex); diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index 6874fcdce62c..c91ea696ec29 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -42,8 +42,11 @@ import com.android.internal.telephony.uicc.IccUtils; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.text.ParseException; +import java.time.Instant; import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.ZoneOffset; +import java.time.ZonedDateTime; /** * A Short Message Service message. @@ -258,12 +261,15 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a destination address and a message + * Gets an SMS-SUBMIT PDU for a destination address and a message. * - * @param scAddress Service Centre address. Null means use default. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is reuested for this message. + * @param header a byte array containing the data for the User Data Header. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. * @hide */ @UnsupportedAppUsage @@ -276,17 +282,19 @@ public class SmsMessage extends SmsMessageBase { /** - * Get an SMS-SUBMIT PDU for a destination address and a message using the - * specified encoding. + * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding. * - * @param scAddress Service Centre address. Null means use default. - * @param encoding Encoding defined by constants in - * com.android.internal.telephony.SmsConstants.ENCODING_* + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is reuested for this message. + * @param header a byte array containing the data for the User Data Header. + * @param encoding encoding defined by constants in + * com.android.internal.telephony.SmsConstants.ENCODING_* * @param languageTable * @param languageShiftTable - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. * @hide */ @UnsupportedAppUsage @@ -299,18 +307,20 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a destination address and a message using the - * specified encoding. + * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding. * - * @param scAddress Service Centre address. Null means use default. - * @param encoding Encoding defined by constants in - * com.android.internal.telephony.SmsConstants.ENCODING_* + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is reuested for this message. + * @param header a byte array containing the data for the User Data Header. + * @param encoding encoding defined by constants in + * com.android.internal.telephony.SmsConstants.ENCODING_* * @param languageTable * @param languageShiftTable * @param validityPeriod Validity Period of the message in Minutes. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. * @hide */ @UnsupportedAppUsage @@ -482,12 +492,14 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a destination address and a message + * Gets an SMS-SUBMIT PDU for a destination address and a message. * - * @param scAddress Service Centre address. Null means use default. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is reuested for this message. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. */ @UnsupportedAppUsage public static SubmitPdu getSubmitPdu(String scAddress, @@ -498,15 +510,15 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a destination address and a message + * Gets an SMS-SUBMIT PDU for a destination address and a message. * - * @param scAddress Service Centre address. Null means use default. - * @param destinationAddress the address of the destination for the message - * @param statusReportRequested staus report of the message Requested + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is reuested for this message. * @param validityPeriod Validity Period of the message in Minutes. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. */ @UnsupportedAppUsage public static SubmitPdu getSubmitPdu(String scAddress, @@ -517,16 +529,15 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a data message to a destination address & port + * Gets an SMS-SUBMIT PDU for a data message to a destination address & port. * - * @param scAddress Service Centre address. null == use default - * @param destinationAddress the address of the destination for the message - * @param destinationPort the port to deliver the message to at the - * destination - * @param data the data for the message - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param destinationPort the port to deliver the message to at the destination. + * @param data the data for the message. + * @param statusReportRequested indicates whether a report is reuested for this message. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. */ public static SubmitPdu getSubmitPdu(String scAddress, String destinationAddress, int destinationPort, byte[] data, @@ -550,8 +561,7 @@ public class SmsMessage extends SmsMessageBase { SubmitPdu ret = new SubmitPdu(); ByteArrayOutputStream bo = getSubmitPduHead( - scAddress, destinationAddress, (byte) 0x41, // MTI = SMS-SUBMIT, - // TP-UDHI = true + scAddress, destinationAddress, (byte) 0x41, /* TP-MTI=SMS-SUBMIT, TP-UDHI=true */ statusReportRequested, ret); // Skip encoding pdu if error occurs when create pdu head and the error will be handled // properly later on encodedMessage sanity check. @@ -578,16 +588,18 @@ public class SmsMessage extends SmsMessageBase { } /** - * Create the beginning of a SUBMIT PDU. This is the part of the - * SUBMIT PDU that is common to the two versions of {@link #getSubmitPdu}, - * one of which takes a byte array and the other of which takes a + * Creates the beginning of a SUBMIT PDU. + * + * This is the part of the SUBMIT PDU that is common to the two versions of + * {@link #getSubmitPdu}, one of which takes a byte array and the other of which takes a * <code>String</code>. * - * @param scAddress Service Centre address. null == use default - * @param destinationAddress the address of the destination for the message + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. * @param mtiByte - * @param ret <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. Returns null on encode error. + * @param statusReportRequested indicates whether a report is reuested for this message. + * @param ret <code>SubmitPdu</code>. + * @return a byte array of the beginning of a SUBMIT PDU. Null for invalid destinationAddress. */ @UnsupportedAppUsage private static ByteArrayOutputStream getSubmitPduHead( @@ -635,6 +647,161 @@ public class SmsMessage extends SmsMessageBase { return bo; } + /** + * Gets an SMS-DELIVER PDU for an originating address and a message. + * + * @param scAddress Service Centre address. Null means use default. + * @param originatingAddress the address of the originating for the message. + * @param message string representation of the message payload. + * @param date the time stamp the message was received. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. + * @hide + */ + public static SubmitPdu getDeliverPdu( + String scAddress, String originatingAddress, String message, long date) { + if (originatingAddress == null || message == null) { + return null; + } + + // Find the best encoding to use + TextEncodingDetails ted = calculateLength(message, false); + int encoding = ted.codeUnitSize; + int languageTable = ted.languageTable; + int languageShiftTable = ted.languageShiftTable; + byte[] header = null; + + if (encoding == ENCODING_7BIT && (languageTable != 0 || languageShiftTable != 0)) { + SmsHeader smsHeader = new SmsHeader(); + smsHeader.languageTable = languageTable; + smsHeader.languageShiftTable = languageShiftTable; + header = SmsHeader.toByteArray(smsHeader); + } + + SubmitPdu ret = new SubmitPdu(); + + 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 + bo.write(0); // SMS-DELIVER + + byte[] oaBytes; + + oaBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(originatingAddress); + + // Return null for invalid originating address + if (oaBytes == null) return null; + + // Originating address length in BCD digits, ignoring TON byte and pad + // TODO Should be better. + bo.write((oaBytes.length - 1) * 2 - ((oaBytes[oaBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0)); + + // Originating Address + bo.write(oaBytes, 0, oaBytes.length); + + // TP-Protocol-Identifier + bo.write(0); + + // User Data (and length) + byte[] userData; + try { + if (encoding == ENCODING_7BIT) { + userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header, + languageTable, languageShiftTable); + } else { // Assume UCS-2 + try { + userData = encodeUCS2(message, header); + } catch (UnsupportedEncodingException uex) { + Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex); + return null; + } + } + } catch (EncodeException ex) { + if (ex.getError() == EncodeException.ERROR_EXCEED_SIZE) { + Rlog.e(LOG_TAG, "Exceed size limitation EncodeException", ex); + return null; + } else { + // 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 (EncodeException ex1) { + Rlog.e(LOG_TAG, "Exceed size limitation EncodeException", ex1); + return null; + } catch (UnsupportedEncodingException uex) { + Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex); + return null; + } + } + } + + if (encoding == ENCODING_7BIT) { + if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) { + // Message too long + Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)"); + return null; + } + // TP-Data-Coding-Scheme + // Default encoding, uncompressed + bo.write(0x00); + } else { // Assume UCS-2 + if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) { + // Message too long + Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)"); + return null; + } + // TP-Data-Coding-Scheme + // UCS-2 encoding, uncompressed + bo.write(0x08); + } + + // TP-Service-Centre-Time-Stamp + byte[] scts = new byte[7]; + + ZonedDateTime zoneDateTime = Instant.ofEpochMilli(date).atZone(ZoneId.systemDefault()); + LocalDateTime localDateTime = zoneDateTime.toLocalDateTime(); + + // It indicates the difference, expressed in quarters of an hour, between the local time and + // GMT. + int timezoneOffset = zoneDateTime.getOffset().getTotalSeconds() / 60 / 15; + boolean negativeOffset = timezoneOffset < 0; + if (negativeOffset) { + timezoneOffset = -timezoneOffset; + } + int year = localDateTime.getYear(); + int month = localDateTime.getMonthValue(); + int day = localDateTime.getDayOfMonth(); + int hour = localDateTime.getHour(); + int minute = localDateTime.getMinute(); + int second = localDateTime.getSecond(); + + year = year > 2000 ? year - 2000 : year - 1900; + scts[0] = (byte) ((((year % 10) & 0x0F) << 4) | ((year / 10) & 0x0F)); + scts[1] = (byte) ((((month % 10) & 0x0F) << 4) | ((month / 10) & 0x0F)); + scts[2] = (byte) ((((day % 10) & 0x0F) << 4) | ((day / 10) & 0x0F)); + scts[3] = (byte) ((((hour % 10) & 0x0F) << 4) | ((hour / 10) & 0x0F)); + scts[4] = (byte) ((((minute % 10) & 0x0F) << 4) | ((minute / 10) & 0x0F)); + scts[5] = (byte) ((((second % 10) & 0x0F) << 4) | ((second / 10) & 0x0F)); + scts[6] = (byte) ((((timezoneOffset % 10) & 0x0F) << 4) | ((timezoneOffset / 10) & 0x0F)); + if (negativeOffset) { + scts[0] |= 0x08; // Negative offset + } + bo.write(scts, 0, scts.length); + + bo.write(userData, 0, userData.length); + ret.encodedMessage = bo.toByteArray(); + return ret; + } + private static class PduParser { @UnsupportedAppUsage byte mPdu[]; |