blob: 2f26bdcbb6a59c8039134182fbd3e46067d4ec5e [file] [log] [blame]
/*
* Copyright (C) 2008 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.cdma;
import android.os.Parcel;
import android.text.format.Time;
import android.util.Config;
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 com.android.internal.telephony.cdma.sms.BearerData;
import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.telephony.cdma.sms.UserData;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Random;
import static android.telephony.SmsMessage.ENCODING_7BIT;
import static android.telephony.SmsMessage.ENCODING_8BIT;
import static android.telephony.SmsMessage.ENCODING_16BIT;
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.MAX_USER_DATA_SEPTETS_WITH_HEADER;
import static android.telephony.SmsMessage.MessageClass;
/**
* A Short Message Service message.
*
*/
public class SmsMessage extends SmsMessageBase {
static final String LOG_TAG = "CDMA";
/**
* Status of a previously submitted SMS.
* This field applies to SMS Delivery Acknowledge messages. 0 indicates success;
* Here, the error class is defined by the bits from 9-8, the status code by the bits from 7-0.
* See C.S0015-B, v2.0, 4.5.21 for a detailed description of possible values.
*/
private int status;
/** The next message ID for the BearerData. Shall be a random value on first use.
* (See C.S0015-B, v2.0, 4.3.1.5)
*/
private static int nextMessageId = 0;
/** Specifies if this is the first SMS message submit */
private static boolean firstSMS = true;
/** Specifies if a return of an acknowledgment is requested for send SMS */
private static final int RETURN_NO_ACK = 0;
private static final int RETURN_ACK = 1;
private SmsEnvelope mEnvelope;
private BearerData mBearerData;
public static class SubmitPdu extends SubmitPduBase {
}
/**
* Create an SmsMessage from a raw PDU.
* Note: In CDMA the PDU is just a byte representation of the received Sms.
*/
public static SmsMessage createFromPdu(byte[] pdu) {
SmsMessage msg = new SmsMessage();
try {
msg.parsePdu(pdu);
return msg;
} catch (RuntimeException ex) {
Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
return null;
}
}
/**
* Note: This function is a GSM specific functionality which is not supported in CDMA mode.
*/
public static SmsMessage newFromCMT(String[] lines) {
Log.w(LOG_TAG, "newFromCMT: is not supported in CDMA mode.");
return null;
}
/**
* Note: This function is a GSM specific functionality which is not supported in CDMA mode.
*/
public static SmsMessage newFromCMTI(String line) {
Log.w(LOG_TAG, "newFromCMTI: is not supported in CDMA mode.");
return null;
}
/**
* Note: This function is a GSM specific functionality which is not supported in CDMA mode.
*/
public static SmsMessage newFromCDS(String line) {
Log.w(LOG_TAG, "newFromCDS: is not supported in CDMA mode.");
return null;
}
/**
* Create a "raw" CDMA SmsMessage from a Parcel that was forged in ril.cpp.
* Note: Only primitive fields are set.
*/
public static SmsMessage newFromParcel(Parcel p) {
// Note: Parcel.readByte actually reads one Int and masks to byte
SmsMessage msg = new SmsMessage();
SmsEnvelope env = new SmsEnvelope();
CdmaSmsAddress addr = new CdmaSmsAddress();
byte[] data;
byte count;
int countInt;
//currently not supported by the modem-lib: env.mMessageType
env.teleService = p.readInt(); //p_cur->uTeleserviceID
if (0 != p.readByte()) { //p_cur->bIsServicePresent
env.messageType = SmsEnvelope.MESSAGE_TYPE_BROADCAST;
}
else {
if (SmsEnvelope.TELESERVICE_NOT_SET == env.teleService) {
// assume type ACK
env.messageType = SmsEnvelope.MESSAGE_TYPE_ACKNOWLEDGE;
} else {
env.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
}
}
env.serviceCategory = p.readInt(); //p_cur->uServicecategory
// address
addr.digitMode = (byte) (0xFF & p.readInt()); //p_cur->sAddress.digit_mode
addr.numberMode = (byte) (0xFF & p.readInt()); //p_cur->sAddress.number_mode
addr.ton = p.readInt(); //p_cur->sAddress.number_type
addr.numberPlan = (byte) (0xFF & p.readInt()); //p_cur->sAddress.number_plan
count = p.readByte(); //p_cur->sAddress.number_of_digits
addr.numberOfDigits = count;
data = new byte[count];
//p_cur->sAddress.digits[digitCount]
for (int index=0; index < count; index++) {
data[index] = p.readByte();
}
addr.origBytes = data;
// ignore subaddress
p.readInt(); //p_cur->sSubAddress.subaddressType
p.readInt(); //p_cur->sSubAddress.odd
count = p.readByte(); //p_cur->sSubAddress.number_of_digits
//p_cur->sSubAddress.digits[digitCount] :
for (int index=0; index < count; index++) {
p.readByte();
}
/* currently not supported by the modem-lib:
env.bearerReply
env.replySeqNo
env.errorClass
env.causeCode
*/
// bearer data
countInt = p.readInt(); //p_cur->uBearerDataLen
if (countInt >0) {
data = new byte[countInt];
//p_cur->aBearerData[digitCount] :
for (int index=0; index < countInt; index++) {
data[index] = p.readByte();
}
env.bearerData = data;
// BD gets further decoded when accessed in SMSDispatcher
}
// link the the filled objects to the SMS
env.origAddress = addr;
msg.originatingAddress = addr;
msg.mEnvelope = env;
// create byte stream representation for transportation through the layers.
msg.createPdu();
return msg;
}
/**
* Create an SmsMessage from an SMS EF record.
*
* @param index Index of SMS record. This should be index in ArrayList
* returned by RuimSmsInterfaceManager.getAllMessagesFromIcc + 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 3GPP2 C.S0023 3.4.27
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;
}
// Second byte is the MSG_LEN, length of the message
// See 3GPP2 C.S0023 3.4.27
int size = data[1];
// Note: Data may include trailing FF's. That's OK; message
// should still parse correctly.
byte[] pdu = new byte[size];
System.arraycopy(data, 2, pdu, 0, size);
// the message has to be parsed before it can be displayed
// see gsm.SmsMessage
return msg;
} catch (RuntimeException ex) {
Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
return null;
}
}
/**
* Note: This function is a GSM specific functionality which is not supported in CDMA mode.
*/
public static int getTPLayerLengthForPDU(String pdu) {
Log.w(LOG_TAG, "getTPLayerLengthForPDU: is not supported in CDMA mode.");
return 0;
}
/**
* Get an SMS-SUBMIT PDU for a destination address and a message
*
* @param scAddress Service Centre address. Null means use default.
* @param destinationAddress Address of the recipient.
* @param message String representation of the message payload.
* @param statusReportRequested Indicates whether a report is requested for this message.
* @param headerData 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.
* @hide
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message,
boolean statusReportRequested, byte[] headerData) {
SmsMessage sms = new SmsMessage();
SubmitPdu ret = new SubmitPdu();
UserData uData = new UserData();
SmsHeader smsHeader;
// Perform null parameter checks.
if (message == null || destinationAddress == null) {
return null;
}
// ** Set UserData + SmsHeader **
try {
// First, try encoding it with the GSM alphabet
int septetCount = GsmAlphabet.countGsmSeptets(message, true);
// User Data (and length)
uData.payload = message.getBytes();
if (uData.payload.length > MAX_USER_DATA_SEPTETS) {
// Message too long
return null;
}
// desired TP-Data-Coding-Scheme
uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
// paddingBits not needed for UD_ENCODING_GSM_7BIT_ALPHABET
// sms header
if(headerData != null) {
smsHeader = SmsHeader.parse(headerData);
uData.userDataHeader = smsHeader;
} else {
// no user data header available!
}
} catch (EncodeException ex) {
byte[] textPart;
// Encoding to the 7-bit alphabet failed. Let's see if we can
// send it as a UCS-2 encoded message
try {
textPart = message.getBytes("utf-16be");
} catch (UnsupportedEncodingException uex) {
Log.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex);
return null;
}
uData.payload = textPart;
if (uData.payload.length > MAX_USER_DATA_BYTES) {
// Message too long
return null;
}
// TP-Data-Coding-Scheme
uData.msgEncoding = UserData.ENCODING_UNICODE_16;
// sms header
if(headerData != null) {
smsHeader = SmsHeader.parse(headerData);
uData.userDataHeader = smsHeader;
} else {
// no user data header available!
}
}
byte[] data = sms.getEnvelope(destinationAddress, statusReportRequested, uData,
(headerData != null), (null == headerData));
if (null == data) return null;
ret.encodedMessage = data;
ret.encodedScAddress = null;
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, short destinationPort, byte[] data,
boolean statusReportRequested) {
SmsMessage sms = new SmsMessage();
SubmitPdu ret = new SubmitPdu();
UserData uData = new UserData();
SmsHeader smsHeader = new SmsHeader();
if (data.length > (MAX_USER_DATA_BYTES - 7 /* UDH size */)) {
Log.e(LOG_TAG, "SMS data message may only contain "
+ (MAX_USER_DATA_BYTES - 7) + " bytes");
return null;
}
byte[] destPort = new byte[4];
destPort[0] = (byte) ((destinationPort >> 8) & 0xFF); // MSB of destination port
destPort[1] = (byte) (destinationPort & 0xFF); // LSB of destination port
destPort[2] = 0x00; // MSB of originating port
destPort[3] = 0x00; // LSB of originating port
smsHeader.add(
new SmsHeader.Element(SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT, destPort));
smsHeader.nbrOfHeaders = smsHeader.getElements().size();
uData.userDataHeader = smsHeader;
// TP-Data-Coding-Scheme
// No class, 8 bit data
uData.msgEncoding = UserData.ENCODING_OCTET;
uData.payload = data;
byte[] msgData = sms.getEnvelope(destinationAddress, statusReportRequested, uData,
true, true);
ret.encodedMessage = msgData;
ret.encodedScAddress = null;
return ret;
}
static class PduParser {
PduParser() {
}
/**
* Parses an SC timestamp and returns a currentTimeMillis()-style
* timestamp
*/
static long getSCTimestampMillis(byte[] timestamp) {
// TP-Service-Centre-Time-Stamp
int year = IccUtils.beBcdByteToInt(timestamp[0]);
int month = IccUtils.beBcdByteToInt(timestamp[1]);
int day = IccUtils.beBcdByteToInt(timestamp[2]);
int hour = IccUtils.beBcdByteToInt(timestamp[3]);
int minute = IccUtils.beBcdByteToInt(timestamp[4]);
int second = IccUtils.beBcdByteToInt(timestamp[5]);
Time time = new Time(Time.TIMEZONE_UTC);
// C.S0015-B v2.0, 4.5.4: range is 1996-2095
time.year = year >= 96 ? year + 1900 : year + 2000;
time.month = month - 1;
time.monthDay = day;
time.hour = hour;
time.minute = minute;
time.second = second;
return time.toMillis(true);
}
}
/**
* Note: This function is a GSM specific functionality which is not supported in CDMA mode.
*/
public int getProtocolIdentifier() {
Log.w(LOG_TAG, "getProtocolIdentifier: is not supported in CDMA mode.");
// (3GPP TS 23.040): "no interworking, but SME to SME protocol":
return 0;
}
/**
* Note: This function is a GSM specific functionality which is not supported in CDMA mode.
*/
public boolean isReplace() {
Log.w(LOG_TAG, "isReplace: is not supported in CDMA mode.");
return false;
}
/**
* {@inheritDoc}
* Note: This function is a GSM specific functionality which is not supported in CDMA mode.
*/
public boolean isCphsMwiMessage() {
Log.w(LOG_TAG, "isCphsMwiMessage: is not supported in CDMA mode.");
return false;
}
/**
* {@inheritDoc}
*/
public boolean isMWIClearMessage() {
if ((mBearerData != null) && (0 == mBearerData.numberOfMessages)) {
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
public boolean isMWISetMessage() {
if ((mBearerData != null) && (mBearerData.numberOfMessages >0)) {
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
public boolean isMwiDontStore() {
if ((mBearerData != null) && (mBearerData.numberOfMessages >0)
&& (null == mBearerData.userData)) {
return true;
}
return false;
}
/**
* Returns the status for a previously submitted message.
* For not interfering with status codes from GSM, this status code is
* shifted to the bits 31-16.
*/
public int getStatus() {
return(status<<16);
}
/**
* Note: This function is a GSM specific functionality which is not supported in CDMA mode.
*/
public boolean isStatusReportMessage() {
Log.w(LOG_TAG, "isStatusReportMessage: is not supported in CDMA mode.");
return false;
}
/**
* Note: This function is a GSM specific functionality which is not supported in CDMA mode.
*/
public boolean isReplyPathPresent() {
Log.w(LOG_TAG, "isReplyPathPresent: is not supported in CDMA mode.");
return false;
}
/**
* Returns the teleservice type of the message.
* @return the teleservice:
* {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_NOT_SET},
* {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WMT},
* {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WEMT},
* {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_VMN},
* {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WAP}
*/
public int getTeleService() {
return mEnvelope.teleService;
}
/**
* Decodes pdu to an empty SMS object.
* In the CDMA case the pdu is just an internal byte stream representation
* of the SMS Java-object.
* @see #createPdu()
*/
private void parsePdu(byte[] pdu) {
ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
DataInputStream dis = new DataInputStream(new BufferedInputStream(bais));
byte length;
SmsEnvelope env = new SmsEnvelope();
CdmaSmsAddress addr = new CdmaSmsAddress();
try {
env.messageType = dis.readInt();
env.teleService = dis.readInt();
env.serviceCategory = dis.readInt();
addr.digitMode = dis.readByte();
addr.numberMode = dis.readByte();
addr.ton = dis.readByte();
addr.numberPlan = dis.readByte();
length = dis.readByte();
addr.numberOfDigits = length;
addr.origBytes = new byte[length];
dis.read(addr.origBytes, 0, length); // digits
env.bearerReply = dis.readInt();
// CauseCode values:
env.replySeqNo = dis.readByte();
env.errorClass = dis.readByte();
env.causeCode = dis.readByte();
//encoded BearerData:
length = dis.readByte();
env.bearerData = new byte[length];
dis.read(env.bearerData, 0, length);
dis.close();
} catch (Exception ex) {
Log.e(LOG_TAG, "createFromPdu: conversion from byte array to object failed: " + ex);
}
// link the filled objects to this SMS
originatingAddress = addr;
env.origAddress = addr;
mEnvelope = env;
parseSms();
}
/**
* Parses a SMS message from its BearerData stream. (mobile-terminated only)
*/
protected void parseSms() {
mBearerData = BearerData.decode(mEnvelope.bearerData);
messageRef = mBearerData.messageId;
// TP-Message-Type-Indicator
// (See 3GPP2 C.S0015-B, v2, 4.5.1)
int messageType = mBearerData.messageType;
switch (messageType) {
case BearerData.MESSAGE_TYPE_USER_ACK:
case BearerData.MESSAGE_TYPE_READ_ACK:
case BearerData.MESSAGE_TYPE_DELIVER:
// Deliver (mobile-terminated only)
parseSmsDeliver();
break;
case BearerData.MESSAGE_TYPE_DELIVERY_ACK:
parseSmsDeliveryAck();
break;
default:
// the rest of these
throw new RuntimeException("Unsupported message type: " + messageType);
}
}
/**
* Parses a SMS-DELIVER message. (mobile-terminated only)
* See 3GPP2 C.S0015-B, v2, 4.4.1
*/
private void parseSmsDeliver() {
if (originatingAddress != null) {
originatingAddress.address = new String(originatingAddress.origBytes);
if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "
+ originatingAddress.address);
}
if (mBearerData.timeStamp != null) {
scTimeMillis = PduParser.getSCTimestampMillis(mBearerData.timeStamp);
}
if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);
parseUserData(mBearerData.userData);
}
/**
* Parses a SMS-DELIVER message. (mobile-terminated only)
* See 3GPP2 C.S0015-B, v2, 4.4.1
*/
private void parseSmsDeliveryAck() {
if (originatingAddress != null) {
originatingAddress.address = new String(originatingAddress.origBytes);
if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "
+ originatingAddress.address);
}
if (mBearerData.timeStamp != null) {
scTimeMillis = PduParser.getSCTimestampMillis(mBearerData.timeStamp);
}
if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);
if (mBearerData.errorClass != BearerData.ERROR_UNDEFINED) {
status = mBearerData.errorClass << 8;
status |= mBearerData.messageStatus;
}
parseUserData(mBearerData.userData);
}
/**
* Parses the User Data of an SMS.
*/
private void parseUserData(UserData uData) {
int encodingType;
if (null == uData) {
return;
}
encodingType = uData.msgEncoding;
// insert DCS-decoding here when type is supported by ril-library
userData = uData.payload;
userDataHeader = uData.userDataHeader;
switch (encodingType) {
case UserData.ENCODING_GSM_7BIT_ALPHABET:
case UserData.ENCODING_UNICODE_16:
// user data was already decoded by wmsts-library
messageBody = new String(userData);
break;
// data and unsupported encodings:
case UserData.ENCODING_OCTET:
default:
messageBody = null;
break;
}
if (Config.LOGV) Log.v(LOG_TAG, "SMS message body (raw): '" + messageBody + "'");
if (messageBody != null) {
parseMessageBody();
}
}
/**
* {@inheritDoc}
*/
public MessageClass getMessageClass() {
if (BearerData.DISPLAY_IMMEDIATE == mBearerData.displayMode ) {
return MessageClass.CLASS_0;
} else {
return MessageClass.UNKNOWN;
}
}
/**
* Creates BearerData and Envelope from parameters for a Submit SMS.
* @return byte stream for SubmitPdu.
*/
private byte[] getEnvelope(String destinationAddress, boolean statusReportRequested,
UserData userData, boolean hasHeaders, boolean useNewId) {
BearerData mBearerData = new BearerData();
SmsEnvelope env = new SmsEnvelope();
CdmaSmsAddress mSmsAddress = new CdmaSmsAddress();
// ** set SmsAddress **
mSmsAddress.digitMode = CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR;
try {
mSmsAddress.origBytes = destinationAddress.getBytes("UTF-8");
} catch (Exception e) {
Log.e(LOG_TAG, "doGetSubmitPdu: conversion of destinationAddress from string to byte[]"
+ " failed: " + e.getMessage());
return null;
}
mSmsAddress.numberOfDigits = (byte)mSmsAddress.origBytes.length;
mSmsAddress.numberMode = CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK;
// see C.S0015-B, v2.0, 3.4.3.3
mSmsAddress.numberPlan = CdmaSmsAddress.NUMBERING_PLAN_ISDN_TELEPHONY;
mSmsAddress.ton = CdmaSmsAddress.TON_INTERNATIONAL_OR_IP;
// ** set BearerData **
mBearerData.userData = userData;
mBearerData.messageType = BearerData.MESSAGE_TYPE_SUBMIT;
if (useNewId) {
setNextMessageId();
}
mBearerData.messageId = nextMessageId;
// Set the reply options (See C.S0015-B, v2.0, 4.5.11)
if(statusReportRequested) {
mBearerData.deliveryAckReq = true;
} else {
mBearerData.deliveryAckReq = false;
}
// Currently settings applications do not support this
mBearerData.userAckReq = false;
mBearerData.readAckReq = false;
mBearerData.reportReq = false;
// Set the display mode (See C.S0015-B, v2.0, 4.5.16)
mBearerData.displayMode = BearerData.DISPLAY_DEFAULT;
// number of messages: not needed for encoding!
// indicate whether a user data header is available
mBearerData.hasUserDataHeader = hasHeaders;
// ** encode BearerData **
byte[] encodedBearerData = null;
try {
encodedBearerData = BearerData.encode(mBearerData);
} catch (Exception e) {
Log.e(LOG_TAG, "doGetSubmitPdu: EncodeCdmaSMS function in JNI interface failed: "
+ e.getMessage());
return null;
}
// ** SmsEnvelope **
env.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
env.teleService = SmsEnvelope.TELESERVICE_WEMT;
env.destAddress = mSmsAddress;
env.bearerReply = RETURN_ACK;
env.bearerData = encodedBearerData;
mEnvelope = env;
// get byte array output stream from SmsAddress object and SmsEnvelope member.
return serialize(mSmsAddress);
}
/**
* Set the nextMessageId to a random value between 0 and 65536
* See C.S0015-B, v2.0, 4.3.1.5
*/
private void setNextMessageId() {
// Message ID, modulo 65536
if(firstSMS) {
Random generator = new Random();
nextMessageId = generator.nextInt(65536);
firstSMS = false;
} else {
nextMessageId = ++nextMessageId & 0xFFFF;
}
}
/**
* Creates ByteArrayOutputStream from CdmaSmsAddress and SmsEnvelope objects
*
* @param address CdmaSmsAddress object
* @return ByteArrayOutputStream
*/
private byte[] serialize(CdmaSmsAddress destAddress) {
SmsEnvelope env = mEnvelope;
ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos));
try {
dos.writeInt(env.teleService);
dos.writeInt(0); //servicePresent
dos.writeInt(0); //serviceCategory
dos.write(destAddress.digitMode);
dos.write(destAddress.numberMode);
dos.write(destAddress.ton); // number_type
dos.write(destAddress.numberPlan);
dos.write(destAddress.numberOfDigits);
dos.write(destAddress.origBytes, 0, destAddress.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(env.bearerData.length);
dos.write(env.bearerData, 0, env.bearerData.length);
dos.close();
return baos.toByteArray();
} catch(IOException ex) {
Log.e(LOG_TAG, "serialize: conversion from object to data output stream failed: " + ex);
return null;
}
}
/**
* Creates byte array (pseudo pdu) from SMS object.
* Note: Do not call this method more than once per object!
*/
private void createPdu() {
SmsEnvelope env = mEnvelope;
CdmaSmsAddress addr = env.origAddress;
ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos));
try {
dos.writeInt(env.messageType);
dos.writeInt(env.teleService);
dos.writeInt(env.serviceCategory);
dos.writeByte(addr.digitMode);
dos.writeByte(addr.numberMode);
dos.writeByte(addr.ton);
dos.writeByte(addr.numberPlan);
dos.writeByte(addr.numberOfDigits);
dos.write(addr.origBytes, 0, addr.origBytes.length); // digits
dos.writeInt(env.bearerReply);
// CauseCode values:
dos.writeByte(env.replySeqNo);
dos.writeByte(env.errorClass);
dos.writeByte(env.causeCode);
//encoded BearerData:
dos.writeByte(env.bearerData.length);
dos.write(env.bearerData, 0, env.bearerData.length);
dos.close();
mPdu = baos.toByteArray();
} catch (IOException ex) {
Log.e(LOG_TAG, "createPdu: conversion from object to byte array failed: " + ex);
}
}
}