summaryrefslogtreecommitdiffstats
path: root/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
diff options
context:
space:
mode:
Diffstat (limited to 'telephony/java/com/android/internal/telephony/gsm/SmsMessage.java')
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java1177
1 files changed, 0 insertions, 1177 deletions
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
deleted file mode 100644
index da60584..0000000
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ /dev/null
@@ -1,1177 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.gsm;
-
-import android.telephony.PhoneNumberUtils;
-import android.text.format.Time;
-import android.util.Log;
-
-import com.android.internal.telephony.EncodeException;
-import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.IccUtils;
-import com.android.internal.telephony.SmsHeader;
-import com.android.internal.telephony.SmsMessageBase;
-
-import java.io.ByteArrayOutputStream;
-import java.io.UnsupportedEncodingException;
-
-import static android.telephony.SmsMessage.ENCODING_16BIT;
-import static android.telephony.SmsMessage.ENCODING_7BIT;
-import static android.telephony.SmsMessage.ENCODING_8BIT;
-import static android.telephony.SmsMessage.ENCODING_KSC5601;
-import static android.telephony.SmsMessage.ENCODING_UNKNOWN;
-import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
-import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
-import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
-import static android.telephony.SmsMessage.MessageClass;
-
-/**
- * A Short Message Service message.
- *
- */
-public class SmsMessage extends SmsMessageBase {
- static final String LOG_TAG = "GSM";
-
- private MessageClass messageClass;
-
- /**
- * TP-Message-Type-Indicator
- * 9.2.3
- */
- private int mti;
-
- /** TP-Protocol-Identifier (TP-PID) */
- private int protocolIdentifier;
-
- // TP-Data-Coding-Scheme
- // see TS 23.038
- private int dataCodingScheme;
-
- // TP-Reply-Path
- // e.g. 23.040 9.2.2.1
- private boolean replyPathPresent = false;
-
- // "Message Marked for Automatic Deletion Group"
- // 23.038 Section 4
- private boolean automaticDeletion;
-
- /** True if Status Report is for SMS-SUBMIT; false for SMS-COMMAND. */
- private boolean forSubmit;
-
- /** The address of the receiver. */
- private GsmSmsAddress recipientAddress;
-
- /** Time when SMS-SUBMIT was delivered from SC to MSE. */
- private long dischargeTimeMillis;
-
- /**
- * 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 status;
-
- /**
- * TP-Status - status of a previously submitted SMS.
- * This field is true iff the message is a SMS-STATUS-REPORT message.
- */
- private boolean isStatusReportMessage = false;
-
- public static class SubmitPdu extends SubmitPduBase {
- }
-
- /**
- * Create an SmsMessage from a raw PDU.
- */
- public static SmsMessage createFromPdu(byte[] pdu) {
- try {
- SmsMessage msg = new SmsMessage();
- msg.parsePdu(pdu);
- return msg;
- } catch (RuntimeException ex) {
- Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
- return null;
- }
- }
-
- /**
- * 3GPP TS 23.040 9.2.3.9 specifies that Type Zero messages are indicated
- * by TP_PID field set to value 0x40
- */
- public boolean isTypeZero() {
- return (protocolIdentifier == 0x40);
- }
-
- /**
- * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
- * +CMT unsolicited response (PDU mode, of course)
- * +CMT: [&lt;alpha>],<length><CR><LF><pdu>
- *
- * Only public for debugging
- *
- * {@hide}
- */
- public static SmsMessage newFromCMT(String[] lines) {
- try {
- SmsMessage msg = new SmsMessage();
- msg.parsePdu(IccUtils.hexStringToBytes(lines[1]));
- return msg;
- } catch (RuntimeException ex) {
- Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
- return null;
- }
- }
-
- /** @hide */
- public static SmsMessage newFromCDS(String line) {
- try {
- SmsMessage msg = new SmsMessage();
- msg.parsePdu(IccUtils.hexStringToBytes(line));
- return msg;
- } catch (RuntimeException ex) {
- Log.e(LOG_TAG, "CDS SMS PDU parsing failed: ", ex);
- return null;
- }
- }
-
- /**
- * Create an SmsMessage from an SMS EF record.
- *
- * @param index Index of SMS record. This should be index in ArrayList
- * returned by SmsManager.getAllMessagesFromSim + 1.
- * @param data Record data.
- * @return An SmsMessage representing the record.
- *
- * @hide
- */
- public static SmsMessage createFromEfRecord(int index, byte[] data) {
- try {
- SmsMessage msg = new SmsMessage();
-
- msg.indexOnIcc = index;
-
- // First byte is status: RECEIVED_READ, RECEIVED_UNREAD, STORED_SENT,
- // or STORED_UNSENT
- // See TS 51.011 10.5.3
- if ((data[0] & 1) == 0) {
- Log.w(LOG_TAG,
- "SMS parsing failed: Trying to parse a free record");
- return null;
- } else {
- msg.statusOnIcc = data[0] & 0x07;
- }
-
- int size = data.length - 1;
-
- // Note: Data may include trailing FF's. That's OK; message
- // should still parse correctly.
- byte[] pdu = new byte[size];
- System.arraycopy(data, 1, pdu, 0, size);
- msg.parsePdu(pdu);
- return msg;
- } catch (RuntimeException ex) {
- Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
- return null;
- }
- }
-
- /**
- * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
- * length in bytes (not hex chars) less the SMSC header
- */
- public static int getTPLayerLengthForPDU(String pdu) {
- int len = pdu.length() / 2;
- int smscLen = Integer.parseInt(pdu.substring(0, 2), 16);
-
- return len - smscLen - 1;
- }
-
- /**
- * Get 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.
- * @hide
- */
- public static SubmitPdu getSubmitPdu(String scAddress,
- String destinationAddress, String message,
- boolean statusReportRequested, byte[] header) {
- return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header,
- ENCODING_UNKNOWN, 0, 0);
- }
-
-
- /**
- * Get 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 android.telephony.SmsMessage.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.
- * @hide
- */
- public static SubmitPdu getSubmitPdu(String scAddress,
- String destinationAddress, String message,
- boolean statusReportRequested, byte[] header, int encoding,
- int languageTable, int languageShiftTable) {
-
- // Perform null parameter checks.
- if (message == null || destinationAddress == null) {
- return null;
- }
-
- if (encoding == ENCODING_UNKNOWN) {
- // Find the best encoding to use
- TextEncodingDetails ted = calculateLength(message, false);
- encoding = ted.codeUnitSize;
- languageTable = ted.languageTable;
- languageShiftTable = ted.languageShiftTable;
-
- if (encoding == ENCODING_7BIT && (languageTable != 0 || languageShiftTable != 0)) {
- if (header != null) {
- SmsHeader smsHeader = SmsHeader.fromByteArray(header);
- if (smsHeader.languageTable != languageTable
- || smsHeader.languageShiftTable != languageShiftTable) {
- Log.w(LOG_TAG, "Updating language table in SMS header: "
- + smsHeader.languageTable + " -> " + languageTable + ", "
- + smsHeader.languageShiftTable + " -> " + languageShiftTable);
- smsHeader.languageTable = languageTable;
- smsHeader.languageShiftTable = languageShiftTable;
- header = SmsHeader.toByteArray(smsHeader);
- }
- } else {
- SmsHeader smsHeader = new SmsHeader();
- smsHeader.languageTable = languageTable;
- smsHeader.languageShiftTable = languageShiftTable;
- header = SmsHeader.toByteArray(smsHeader);
- }
- }
- }
-
- SubmitPdu ret = new SubmitPdu();
- // MTI = SMS-SUBMIT, UDHI = header != null
- byte mtiByte = (byte)(0x01 | (header != null ? 0x40 : 0x00));
- ByteArrayOutputStream bo = getSubmitPduHead(
- scAddress, destinationAddress, mtiByte,
- statusReportRequested, ret);
-
- // 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) {
- Log.e(LOG_TAG,
- "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(LOG_TAG,
- "Implausible UnsupportedEncodingException ",
- uex);
- return null;
- }
- }
-
- if (encoding == ENCODING_7BIT) {
- if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
- // Message too long
- Log.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)");
- return null;
- }
- // TP-Data-Coding-Scheme
- // Default encoding, uncompressed
- // To test writing messages to the SIM card, change this value 0x00
- // to 0x12, which means "bits 1 and 0 contain message class, and the
- // class is 2". Note that this takes effect for the sender. In other
- // words, messages sent by the phone with this change will end up on
- // the receiver's SIM card. You can then send messages to yourself
- // (on a phone with this change) and they'll end up on the SIM card.
- bo.write(0x00);
- } else { // assume UCS-2
- if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
- // Message too long
- Log.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)");
- return null;
- }
- // TP-Data-Coding-Scheme
- // UCS-2 encoding, uncompressed
- bo.write(0x08);
- }
-
- // (no TP-Validity-Period)
- bo.write(userData, 0, userData.length);
- ret.encodedMessage = bo.toByteArray();
- return ret;
- }
-
- /**
- * Packs header and UCS-2 encoded message. Includes TP-UDL & TP-UDHL if necessary
- *
- * @return
- * @throws UnsupportedEncodingException
- */
- private static 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;
- }
-
- /**
- * Get 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.
- */
- public static SubmitPdu getSubmitPdu(String scAddress,
- String destinationAddress, String message,
- boolean statusReportRequested) {
-
- return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null);
- }
-
- /**
- * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; 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.
- */
- public static SubmitPdu getSubmitPdu(String scAddress,
- String destinationAddress, int destinationPort, byte[] data,
- boolean statusReportRequested) {
-
- SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
- portAddrs.destPort = destinationPort;
- portAddrs.origPort = 0;
- portAddrs.areEightBits = false;
-
- SmsHeader smsHeader = new SmsHeader();
- smsHeader.portAddrs = portAddrs;
-
- byte[] smsHeaderData = SmsHeader.toByteArray(smsHeader);
-
- if ((data.length + smsHeaderData.length + 1) > MAX_USER_DATA_BYTES) {
- Log.e(LOG_TAG, "SMS data message may only contain "
- + (MAX_USER_DATA_BYTES - smsHeaderData.length - 1) + " bytes");
- return null;
- }
-
- SubmitPdu ret = new SubmitPdu();
- ByteArrayOutputStream bo = getSubmitPduHead(
- scAddress, destinationAddress, (byte) 0x41, // MTI = SMS-SUBMIT,
- // TP-UDHI = true
- statusReportRequested, ret);
-
- // TP-Data-Coding-Scheme
- // No class, 8 bit data
- bo.write(0x04);
-
- // (no TP-Validity-Period)
-
- // Total size
- bo.write(data.length + smsHeaderData.length + 1);
-
- // User data header
- bo.write(smsHeaderData.length);
- bo.write(smsHeaderData, 0, smsHeaderData.length);
-
- // User data
- bo.write(data, 0, data.length);
-
- ret.encodedMessage = bo.toByteArray();
- return ret;
- }
-
- /**
- * 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
- * <code>String</code>.
- *
- * @param scAddress Service Centre address. null == 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
- */
- private static ByteArrayOutputStream getSubmitPduHead(
- String scAddress, String destinationAddress, byte mtiByte,
- 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 (false) Log.d(LOG_TAG, "SMS status report requested");
- }
- bo.write(mtiByte);
-
- // 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 static class PduParser {
- byte pdu[];
- int cur;
- SmsHeader userDataHeader;
- byte[] userData;
- int mUserDataSeptetPadding;
- int mUserDataSize;
-
- PduParser(byte[] pdu) {
- this.pdu = pdu;
- cur = 0;
- mUserDataSeptetPadding = 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(pdu, cur, len);
- } catch (RuntimeException tr) {
- Log.d(LOG_TAG, "invalid SC address: ", tr);
- ret = null;
- }
- }
-
- cur += len;
-
- return ret;
- }
-
- /**
- * returns non-sign-extended byte value
- */
- int getByte() {
- return pdu[cur++] & 0xff;
- }
-
- /**
- * Any address except the SC address (eg, originating address) See TS
- * 23.040 9.1.2.5
- */
- 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 = pdu[cur] & 0xff;
- int lengthBytes = 2 + (addressLength + 1) / 2;
-
- ret = new GsmSmsAddress(pdu, cur, lengthBytes);
-
- cur += lengthBytes;
-
- return ret;
- }
-
- /**
- * Parses an SC timestamp and returns a currentTimeMillis()-style
- * timestamp
- */
-
- long getSCTimestampMillis() {
- // TP-Service-Centre-Time-Stamp
- int year = IccUtils.gsmBcdByteToInt(pdu[cur++]);
- int month = IccUtils.gsmBcdByteToInt(pdu[cur++]);
- int day = IccUtils.gsmBcdByteToInt(pdu[cur++]);
- int hour = IccUtils.gsmBcdByteToInt(pdu[cur++]);
- int minute = IccUtils.gsmBcdByteToInt(pdu[cur++]);
- int second = IccUtils.gsmBcdByteToInt(pdu[cur++]);
-
- // 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 = pdu[cur++];
-
- // Mask out sign bit.
- int timezoneOffset = IccUtils.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);
- }
-
- /**
- * Pulls the user data out of the PDU, and separates the payload from
- * the header if there is one.
- *
- * @param hasUserDataHeader true if there is a user data header
- * @param dataInSeptets true if the data payload is in septets instead
- * of octets
- * @return the number of septets or octets in the user data payload
- */
- int constructUserData(boolean hasUserDataHeader, boolean dataInSeptets) {
- int offset = cur;
- int userDataLength = pdu[offset++] & 0xff;
- int headerSeptets = 0;
- int userDataHeaderLength = 0;
-
- if (hasUserDataHeader) {
- userDataHeaderLength = pdu[offset++] & 0xff;
-
- byte[] udh = new byte[userDataHeaderLength];
- System.arraycopy(pdu, offset, udh, 0, userDataHeaderLength);
- userDataHeader = SmsHeader.fromByteArray(udh);
- offset += userDataHeaderLength;
-
- int headerBits = (userDataHeaderLength + 1) * 8;
- headerSeptets = headerBits / 7;
- headerSeptets += (headerBits % 7) > 0 ? 1 : 0;
- mUserDataSeptetPadding = (headerSeptets * 7) - headerBits;
- }
-
- int bufferLen;
- if (dataInSeptets) {
- /*
- * Here we just create the user data length to be the remainder of
- * the pdu minus the user data header, since userDataLength means
- * the number of uncompressed septets.
- */
- bufferLen = pdu.length - offset;
- } else {
- /*
- * userDataLength is the count of octets, so just subtract the
- * user data header.
- */
- bufferLen = userDataLength - (hasUserDataHeader ? (userDataHeaderLength + 1) : 0);
- if (bufferLen < 0) {
- bufferLen = 0;
- }
- }
-
- userData = new byte[bufferLen];
- System.arraycopy(pdu, offset, userData, 0, userData.length);
- cur = offset;
-
- if (dataInSeptets) {
- // Return the number of septets
- int count = userDataLength - headerSeptets;
- // If count < 0, return 0 (means UDL was probably incorrect)
- return count < 0 ? 0 : count;
- } else {
- // Return the number of octets
- return userData.length;
- }
- }
-
- /**
- * Returns the user data payload, not including the headers
- *
- * @return the user data payload, not including the headers
- */
- byte[] getUserData() {
- return userData;
- }
-
- /**
- * Returns the number of padding bits at the beginning of the user data
- * array before the start of the septets.
- *
- * @return the number of padding bits at the beginning of the user data
- * array before the start of the septets
- */
- int getUserDataSeptetPadding() {
- return mUserDataSeptetPadding;
- }
-
- /**
- * Returns an object representing the user data headers
- *
- * {@hide}
- */
- SmsHeader getUserDataHeader() {
- return userDataHeader;
- }
-
- /**
- * Interprets the user data payload as packed GSM 7bit characters, and
- * decodes them into a String.
- *
- * @param septetCount the number of septets in the user data payload
- * @return a String with the decoded characters
- */
- String getUserDataGSM7Bit(int septetCount, int languageTable,
- int languageShiftTable) {
- String ret;
-
- ret = GsmAlphabet.gsm7BitPackedToString(pdu, cur, septetCount,
- mUserDataSeptetPadding, languageTable, languageShiftTable);
-
- cur += (septetCount * 7) / 8;
-
- return ret;
- }
-
- /**
- * Interprets the user data payload as UCS2 characters, and
- * decodes them into a String.
- *
- * @param byteCount the number of bytes in the user data payload
- * @return a String with the decoded characters
- */
- String getUserDataUCS2(int byteCount) {
- String ret;
-
- try {
- ret = new String(pdu, cur, byteCount, "utf-16");
- } catch (UnsupportedEncodingException ex) {
- ret = "";
- Log.e(LOG_TAG, "implausible UnsupportedEncodingException", ex);
- }
-
- cur += byteCount;
- return ret;
- }
-
- /**
- * Interprets the user data payload as KSC-5601 characters, and
- * decodes them into a String.
- *
- * @param byteCount the number of bytes in the user data payload
- * @return a String with the decoded characters
- */
- String getUserDataKSC5601(int byteCount) {
- String ret;
-
- try {
- ret = new String(pdu, cur, byteCount, "KSC5601");
- } catch (UnsupportedEncodingException ex) {
- ret = "";
- Log.e(LOG_TAG, "implausible UnsupportedEncodingException", ex);
- }
-
- cur += byteCount;
- return ret;
- }
-
- boolean moreDataPresent() {
- return (pdu.length > cur);
- }
- }
-
- /**
- * Calculate the number of septets needed to encode the message.
- *
- * @param msgBody the message to encode
- * @param use7bitOnly ignore (but still count) illegal characters if true
- * @return TextEncodingDetails
- */
- public static TextEncodingDetails calculateLength(CharSequence msgBody,
- boolean use7bitOnly) {
- TextEncodingDetails ted = GsmAlphabet.countGsmSeptets(msgBody, use7bitOnly);
- if (ted == null) {
- ted = new TextEncodingDetails();
- int octets = msgBody.length() * 2;
- ted.codeUnitCount = msgBody.length();
- if (octets > MAX_USER_DATA_BYTES) {
- ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
- MAX_USER_DATA_BYTES_WITH_HEADER;
- ted.codeUnitsRemaining = ((ted.msgCount *
- MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
- } else {
- ted.msgCount = 1;
- ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
- }
- ted.codeUnitSize = ENCODING_16BIT;
- }
- return ted;
- }
-
- /** {@inheritDoc} */
- @Override
- public int getProtocolIdentifier() {
- return protocolIdentifier;
- }
-
- /**
- * Returns the TP-Data-Coding-Scheme byte, for acknowledgement of SMS-PP download messages.
- * @return the TP-DCS field of the SMS header
- */
- int getDataCodingScheme() {
- return dataCodingScheme;
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isReplace() {
- return (protocolIdentifier & 0xc0) == 0x40
- && (protocolIdentifier & 0x3f) > 0
- && (protocolIdentifier & 0x3f) < 8;
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isCphsMwiMessage() {
- return ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageClear()
- || ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageSet();
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isMWIClearMessage() {
- if (isMwi && !mwiSense) {
- return true;
- }
-
- return originatingAddress != null
- && ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageClear();
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isMWISetMessage() {
- if (isMwi && mwiSense) {
- return true;
- }
-
- return originatingAddress != null
- && ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageSet();
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isMwiDontStore() {
- if (isMwi && mwiDontStore) {
- return true;
- }
-
- if (isCphsMwiMessage()) {
- // See CPHS 4.2 Section B.4.2.1
- // If the user data is a single space char, do not store
- // the message. Otherwise, store and display as usual
- if (" ".equals(getMessageBody())) {
- ;
- }
- return true;
- }
-
- return false;
- }
-
- /** {@inheritDoc} */
- @Override
- public int getStatus() {
- return status;
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isStatusReportMessage() {
- return isStatusReportMessage;
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isReplyPathPresent() {
- return replyPathPresent;
- }
-
- /**
- * 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...
- */
- private void parsePdu(byte[] pdu) {
- mPdu = pdu;
- // Log.d(LOG_TAG, "raw sms message:");
- // Log.d(LOG_TAG, s);
-
- PduParser p = new PduParser(pdu);
-
- scAddress = p.getSCAddress();
-
- if (scAddress != null) {
- if (false) Log.d(LOG_TAG, "SMS SC address: " + scAddress);
- }
-
- // TODO(mkf) support reply path, user data header indicator
-
- // TP-Message-Type-Indicator
- // 9.2.3
- int firstByte = p.getByte();
-
- mti = firstByte & 0x3;
- switch (mti) {
- // 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 2:
- parseSmsStatusReport(p, firstByte);
- break;
- default:
- // TODO(mkf) the rest of these
- 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) {
- isStatusReportMessage = true;
-
- // TP-Status-Report-Qualifier bit == 0 for SUBMIT
- forSubmit = (firstByte & 0x20) == 0x00;
- // TP-Message-Reference
- messageRef = p.getByte();
- // TP-Recipient-Address
- recipientAddress = p.getAddress();
- // TP-Service-Centre-Time-Stamp
- scTimeMillis = p.getSCTimestampMillis();
- // TP-Discharge-Time
- dischargeTimeMillis = p.getSCTimestampMillis();
- // TP-Status
- status = 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();
- }
- // TP-Protocol-Identifier
- if ((extraParams & 0x01) != 0) {
- protocolIdentifier = p.getByte();
- }
- // TP-Data-Coding-Scheme
- if ((extraParams & 0x02) != 0) {
- dataCodingScheme = p.getByte();
- }
- // TP-User-Data-Length (implies existence of TP-User-Data)
- if ((extraParams & 0x04) != 0) {
- boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
- parseUserData(p, hasUserDataHeader);
- }
- }
- }
-
- private void parseSmsDeliver(PduParser p, int firstByte) {
- replyPathPresent = (firstByte & 0x80) == 0x80;
-
- originatingAddress = p.getAddress();
-
- if (originatingAddress != null) {
- if (false) Log.v(LOG_TAG, "SMS originating address: "
- + originatingAddress.address);
- }
-
- // TP-Protocol-Identifier (TP-PID)
- // TS 23.040 9.2.3.9
- protocolIdentifier = p.getByte();
-
- // TP-Data-Coding-Scheme
- // see TS 23.038
- dataCodingScheme = p.getByte();
-
- if (false) {
- Log.v(LOG_TAG, "SMS TP-PID:" + protocolIdentifier
- + " data coding scheme: " + dataCodingScheme);
- }
-
- scTimeMillis = p.getSCTimestampMillis();
-
- if (false) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);
-
- boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
-
- parseUserData(p, hasUserDataHeader);
- }
-
- /**
- * Parses the User Data of an SMS.
- *
- * @param p The current PduParser.
- * @param hasUserDataHeader Indicates whether a header is present in the
- * User Data.
- */
- private void parseUserData(PduParser p, boolean hasUserDataHeader) {
- boolean hasMessageClass = false;
- boolean userDataCompressed = false;
-
- int encodingType = ENCODING_UNKNOWN;
-
- // Look up the data encoding scheme
- if ((dataCodingScheme & 0x80) == 0) {
- // Bits 7..4 == 0xxx
- automaticDeletion = (0 != (dataCodingScheme & 0x40));
- userDataCompressed = (0 != (dataCodingScheme & 0x20));
- hasMessageClass = (0 != (dataCodingScheme & 0x10));
-
- if (userDataCompressed) {
- Log.w(LOG_TAG, "4 - Unsupported SMS data coding scheme "
- + "(compression) " + (dataCodingScheme & 0xff));
- } else {
- switch ((dataCodingScheme >> 2) & 0x3) {
- case 0: // GSM 7 bit default alphabet
- encodingType = ENCODING_7BIT;
- break;
-
- case 2: // UCS 2 (16bit)
- encodingType = ENCODING_16BIT;
- break;
-
- case 1: // 8 bit data
- case 3: // reserved
- Log.w(LOG_TAG, "1 - Unsupported SMS data coding scheme "
- + (dataCodingScheme & 0xff));
- encodingType = ENCODING_8BIT;
- break;
- }
- }
- } else if ((dataCodingScheme & 0xf0) == 0xf0) {
- automaticDeletion = false;
- hasMessageClass = true;
- userDataCompressed = false;
-
- if (0 == (dataCodingScheme & 0x04)) {
- // GSM 7 bit default alphabet
- encodingType = ENCODING_7BIT;
- } else {
- // 8 bit data
- encodingType = ENCODING_8BIT;
- }
- } else if ((dataCodingScheme & 0xF0) == 0xC0
- || (dataCodingScheme & 0xF0) == 0xD0
- || (dataCodingScheme & 0xF0) == 0xE0) {
- // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
-
- // 0xC0 == 7 bit, don't store
- // 0xD0 == 7 bit, store
- // 0xE0 == UCS-2, store
-
- if ((dataCodingScheme & 0xF0) == 0xE0) {
- encodingType = ENCODING_16BIT;
- } else {
- encodingType = ENCODING_7BIT;
- }
-
- userDataCompressed = false;
- boolean active = ((dataCodingScheme & 0x08) == 0x08);
-
- // bit 0x04 reserved
-
- if ((dataCodingScheme & 0x03) == 0x00) {
- isMwi = true;
- mwiSense = active;
- mwiDontStore = ((dataCodingScheme & 0xF0) == 0xC0);
- } else {
- isMwi = false;
-
- Log.w(LOG_TAG, "MWI for fax, email, or other "
- + (dataCodingScheme & 0xff));
- }
- } else if ((dataCodingScheme & 0xC0) == 0x80) {
- // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
- // 0x80..0xBF == Reserved coding groups
- if (dataCodingScheme == 0x84) {
- // This value used for KSC5601 by carriers in Korea.
- encodingType = ENCODING_KSC5601;
- } else {
- Log.w(LOG_TAG, "5 - Unsupported SMS data coding scheme "
- + (dataCodingScheme & 0xff));
- }
- } else {
- Log.w(LOG_TAG, "3 - Unsupported SMS data coding scheme "
- + (dataCodingScheme & 0xff));
- }
-
- // set both the user data and the user data header.
- int count = p.constructUserData(hasUserDataHeader,
- encodingType == ENCODING_7BIT);
- this.userData = p.getUserData();
- this.userDataHeader = p.getUserDataHeader();
-
- switch (encodingType) {
- case ENCODING_UNKNOWN:
- case ENCODING_8BIT:
- messageBody = null;
- break;
-
- case ENCODING_7BIT:
- messageBody = p.getUserDataGSM7Bit(count,
- hasUserDataHeader ? userDataHeader.languageTable : 0,
- hasUserDataHeader ? userDataHeader.languageShiftTable : 0);
- break;
-
- case ENCODING_16BIT:
- messageBody = p.getUserDataUCS2(count);
- break;
-
- case ENCODING_KSC5601:
- messageBody = p.getUserDataKSC5601(count);
- break;
- }
-
- if (false) Log.v(LOG_TAG, "SMS message body (raw): '" + messageBody + "'");
-
- if (messageBody != null) {
- parseMessageBody();
- }
-
- if (!hasMessageClass) {
- messageClass = MessageClass.UNKNOWN;
- } else {
- switch (dataCodingScheme & 0x3) {
- case 0:
- messageClass = MessageClass.CLASS_0;
- break;
- case 1:
- messageClass = MessageClass.CLASS_1;
- break;
- case 2:
- messageClass = MessageClass.CLASS_2;
- break;
- case 3:
- messageClass = MessageClass.CLASS_3;
- break;
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public MessageClass getMessageClass() {
- return messageClass;
- }
-
- /**
- * Returns true if this is a (U)SIM data download type SM.
- * See 3GPP TS 31.111 section 9.1 and TS 23.040 section 9.2.3.9.
- *
- * @return true if this is a USIM data download message; false otherwise
- */
- boolean isUsimDataDownload() {
- return messageClass == MessageClass.CLASS_2 &&
- (protocolIdentifier == 0x7f || protocolIdentifier == 0x7c);
- }
-}