SmsMessage.java revision 8b53bb26569395511e3dbd5f94bda74ea6b9e37c
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.internal.telephony.cdma; 18 19import android.os.Parcel; 20import android.os.SystemProperties; 21import android.telephony.PhoneNumberUtils; 22import android.telephony.SmsCbLocation; 23import android.telephony.SmsCbMessage; 24import android.telephony.cdma.CdmaSmsCbProgramData; 25import android.telephony.Rlog; 26import android.util.Log; 27import android.text.TextUtils; 28import android.content.res.Resources; 29 30import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; 31import com.android.internal.telephony.SmsConstants; 32import com.android.internal.telephony.SmsHeader; 33import com.android.internal.telephony.SmsMessageBase; 34import com.android.internal.telephony.TelephonyProperties; 35import com.android.internal.telephony.cdma.sms.BearerData; 36import com.android.internal.telephony.cdma.sms.CdmaSmsAddress; 37import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress; 38import com.android.internal.telephony.cdma.sms.SmsEnvelope; 39import com.android.internal.telephony.cdma.sms.UserData; 40import com.android.internal.telephony.uicc.IccUtils; 41import com.android.internal.util.BitwiseInputStream; 42import com.android.internal.util.HexDump; 43import com.android.internal.telephony.Sms7BitEncodingTranslator; 44 45import java.io.BufferedOutputStream; 46import java.io.ByteArrayInputStream; 47import java.io.ByteArrayOutputStream; 48import java.io.DataInputStream; 49import java.io.DataOutputStream; 50import java.io.IOException; 51import java.util.ArrayList; 52 53/** 54 * TODO(cleanup): these constants are disturbing... are they not just 55 * different interpretations on one number? And if we did not have 56 * terrible class name overlap, they would not need to be directly 57 * imported like this. The class in this file could just as well be 58 * named CdmaSmsMessage, could it not? 59 */ 60 61/** 62 * TODO(cleanup): internally returning null in many places makes 63 * debugging very hard (among many other reasons) and should be made 64 * more meaningful (replaced with exceptions for example). Null 65 * returns should only occur at the very outside of the module/class 66 * scope. 67 */ 68 69/** 70 * A Short Message Service message. 71 * 72 */ 73public class SmsMessage extends SmsMessageBase { 74 static final String LOG_TAG = "SmsMessage"; 75 static private final String LOGGABLE_TAG = "CDMA:SMS"; 76 private static final boolean VDBG = false; 77 78 private final static byte TELESERVICE_IDENTIFIER = 0x00; 79 private final static byte SERVICE_CATEGORY = 0x01; 80 private final static byte ORIGINATING_ADDRESS = 0x02; 81 private final static byte ORIGINATING_SUB_ADDRESS = 0x03; 82 private final static byte DESTINATION_ADDRESS = 0x04; 83 private final static byte DESTINATION_SUB_ADDRESS = 0x05; 84 private final static byte BEARER_REPLY_OPTION = 0x06; 85 private final static byte CAUSE_CODES = 0x07; 86 private final static byte BEARER_DATA = 0x08; 87 88 /** 89 * Status of a previously submitted SMS. 90 * This field applies to SMS Delivery Acknowledge messages. 0 indicates success; 91 * Here, the error class is defined by the bits from 9-8, the status code by the bits from 7-0. 92 * See C.S0015-B, v2.0, 4.5.21 for a detailed description of possible values. 93 */ 94 private int status; 95 96 /** Specifies if a return of an acknowledgment is requested for send SMS */ 97 private static final int RETURN_NO_ACK = 0; 98 private static final int RETURN_ACK = 1; 99 100 private SmsEnvelope mEnvelope; 101 private BearerData mBearerData; 102 103 public static class SubmitPdu extends SubmitPduBase { 104 } 105 106 /** 107 * Create an SmsMessage from a raw PDU. 108 * Note: In CDMA the PDU is just a byte representation of the received Sms. 109 */ 110 public static SmsMessage createFromPdu(byte[] pdu) { 111 SmsMessage msg = new SmsMessage(); 112 113 try { 114 msg.parsePdu(pdu); 115 return msg; 116 } catch (RuntimeException ex) { 117 Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex); 118 return null; 119 } catch (OutOfMemoryError e) { 120 Log.e(LOG_TAG, "SMS PDU parsing failed with out of memory: ", e); 121 return null; 122 } 123 } 124 125 /** 126 * Create a "raw" CDMA SmsMessage from a Parcel that was forged in ril.cpp. 127 * Note: Only primitive fields are set. 128 */ 129 public static SmsMessage newFromParcel(Parcel p) { 130 // Note: Parcel.readByte actually reads one Int and masks to byte 131 SmsMessage msg = new SmsMessage(); 132 SmsEnvelope env = new SmsEnvelope(); 133 CdmaSmsAddress addr = new CdmaSmsAddress(); 134 CdmaSmsSubaddress subaddr = new CdmaSmsSubaddress(); 135 byte[] data; 136 byte count; 137 int countInt; 138 int addressDigitMode; 139 140 //currently not supported by the modem-lib: env.mMessageType 141 env.teleService = p.readInt(); //p_cur->uTeleserviceID 142 143 if (0 != p.readByte()) { //p_cur->bIsServicePresent 144 env.messageType = SmsEnvelope.MESSAGE_TYPE_BROADCAST; 145 } 146 else { 147 if (SmsEnvelope.TELESERVICE_NOT_SET == env.teleService) { 148 // assume type ACK 149 env.messageType = SmsEnvelope.MESSAGE_TYPE_ACKNOWLEDGE; 150 } else { 151 env.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT; 152 } 153 } 154 env.serviceCategory = p.readInt(); //p_cur->uServicecategory 155 156 // address 157 addressDigitMode = p.readInt(); 158 addr.digitMode = (byte) (0xFF & addressDigitMode); //p_cur->sAddress.digit_mode 159 addr.numberMode = (byte) (0xFF & p.readInt()); //p_cur->sAddress.number_mode 160 addr.ton = p.readInt(); //p_cur->sAddress.number_type 161 addr.numberPlan = (byte) (0xFF & p.readInt()); //p_cur->sAddress.number_plan 162 count = p.readByte(); //p_cur->sAddress.number_of_digits 163 addr.numberOfDigits = count; 164 data = new byte[count]; 165 //p_cur->sAddress.digits[digitCount] 166 for (int index=0; index < count; index++) { 167 data[index] = p.readByte(); 168 169 // convert the value if it is 4-bit DTMF to 8 bit 170 if (addressDigitMode == CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF) { 171 data[index] = msg.convertDtmfToAscii(data[index]); 172 } 173 } 174 175 addr.origBytes = data; 176 177 subaddr.type = p.readInt(); // p_cur->sSubAddress.subaddressType 178 subaddr.odd = p.readByte(); // p_cur->sSubAddress.odd 179 count = p.readByte(); // p_cur->sSubAddress.number_of_digits 180 181 if (count < 0) { 182 count = 0; 183 } 184 185 // p_cur->sSubAddress.digits[digitCount] : 186 187 data = new byte[count]; 188 189 for (int index = 0; index < count; ++index) { 190 data[index] = p.readByte(); 191 } 192 193 subaddr.origBytes = data; 194 195 /* currently not supported by the modem-lib: 196 env.bearerReply 197 env.replySeqNo 198 env.errorClass 199 env.causeCode 200 */ 201 202 // bearer data 203 countInt = p.readInt(); //p_cur->uBearerDataLen 204 if (countInt < 0) { 205 countInt = 0; 206 } 207 208 data = new byte[countInt]; 209 for (int index=0; index < countInt; index++) { 210 data[index] = p.readByte(); 211 } 212 // BD gets further decoded when accessed in SMSDispatcher 213 env.bearerData = data; 214 215 // link the the filled objects to the SMS 216 env.origAddress = addr; 217 env.origSubaddress = subaddr; 218 msg.mOriginatingAddress = addr; 219 msg.mEnvelope = env; 220 221 // create byte stream representation for transportation through the layers. 222 msg.createPdu(); 223 224 return msg; 225 } 226 227 /** 228 * Create an SmsMessage from an SMS EF record. 229 * 230 * @param index Index of SMS record. This should be index in ArrayList 231 * returned by RuimSmsInterfaceManager.getAllMessagesFromIcc + 1. 232 * @param data Record data. 233 * @return An SmsMessage representing the record. 234 * 235 * @hide 236 */ 237 public static SmsMessage createFromEfRecord(int index, byte[] data) { 238 try { 239 SmsMessage msg = new SmsMessage(); 240 241 msg.mIndexOnIcc = index; 242 243 // First byte is status: RECEIVED_READ, RECEIVED_UNREAD, STORED_SENT, 244 // or STORED_UNSENT 245 // See 3GPP2 C.S0023 3.4.27 246 if ((data[0] & 1) == 0) { 247 Rlog.w(LOG_TAG, "SMS parsing failed: Trying to parse a free record"); 248 return null; 249 } else { 250 msg.mStatusOnIcc = data[0] & 0x07; 251 } 252 253 // Second byte is the MSG_LEN, length of the message 254 // See 3GPP2 C.S0023 3.4.27 255 int size = data[1]; 256 257 // Note: Data may include trailing FF's. That's OK; message 258 // should still parse correctly. 259 byte[] pdu = new byte[size]; 260 System.arraycopy(data, 2, pdu, 0, size); 261 // the message has to be parsed before it can be displayed 262 // see gsm.SmsMessage 263 msg.parsePduFromEfRecord(pdu); 264 return msg; 265 } catch (RuntimeException ex) { 266 Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex); 267 return null; 268 } 269 270 } 271 272 /** 273 * Note: This function is a GSM specific functionality which is not supported in CDMA mode. 274 */ 275 public static int getTPLayerLengthForPDU(String pdu) { 276 Rlog.w(LOG_TAG, "getTPLayerLengthForPDU: is not supported in CDMA mode."); 277 return 0; 278 } 279 280 /** 281 * TODO(cleanup): why do getSubmitPdu methods take an scAddr input 282 * and do nothing with it? GSM allows us to specify a SC (eg, 283 * when responding to an SMS that explicitly requests the response 284 * is sent to a specific SC), or pass null to use the default 285 * value. Is there no similar notion in CDMA? Or do we just not 286 * have it hooked up? 287 */ 288 289 /** 290 * Get an SMS-SUBMIT PDU for a destination address and a message 291 * 292 * @param scAddr Service Centre address. Null means use default. 293 * @param destAddr Address of the recipient. 294 * @param message String representation of the message payload. 295 * @param statusReportRequested Indicates whether a report is requested for this message. 296 * @param smsHeader Array containing the data for the User Data Header, preceded 297 * by the Element Identifiers. 298 * @return a <code>SubmitPdu</code> containing the encoded SC 299 * address, if applicable, and the encoded message. 300 * Returns null on encode error. 301 * @hide 302 */ 303 public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message, 304 boolean statusReportRequested, SmsHeader smsHeader) { 305 306 /** 307 * TODO(cleanup): Do we really want silent failure like this? 308 * Would it not be much more reasonable to make sure we don't 309 * call this function if we really want nothing done? 310 */ 311 if (message == null || destAddr == null) { 312 return null; 313 } 314 315 UserData uData = new UserData(); 316 uData.payloadStr = message; 317 uData.userDataHeader = smsHeader; 318 return privateGetSubmitPdu(destAddr, statusReportRequested, uData); 319 } 320 321 /** 322 * Get an SMS-SUBMIT PDU for a data message to a destination address and port. 323 * 324 * @param scAddr Service Centre address. null == use default 325 * @param destAddr the address of the destination for the message 326 * @param destPort the port to deliver the message to at the 327 * destination 328 * @param data the data for the message 329 * @return a <code>SubmitPdu</code> containing the encoded SC 330 * address, if applicable, and the encoded message. 331 * Returns null on encode error. 332 */ 333 public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, int destPort, 334 byte[] data, boolean statusReportRequested) { 335 336 /** 337 * TODO(cleanup): this is not a general-purpose SMS creation 338 * method, but rather something specialized to messages 339 * containing OCTET encoded (meaning non-human-readable) user 340 * data. The name should reflect that, and not just overload. 341 */ 342 343 SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs(); 344 portAddrs.destPort = destPort; 345 portAddrs.origPort = 0; 346 portAddrs.areEightBits = false; 347 348 SmsHeader smsHeader = new SmsHeader(); 349 smsHeader.portAddrs = portAddrs; 350 351 UserData uData = new UserData(); 352 uData.userDataHeader = smsHeader; 353 uData.msgEncoding = UserData.ENCODING_OCTET; 354 uData.msgEncodingSet = true; 355 uData.payload = data; 356 357 return privateGetSubmitPdu(destAddr, statusReportRequested, uData); 358 } 359 360 /** 361 * Get an SMS-SUBMIT PDU for a data message to a destination address & port 362 * 363 * @param destAddr the address of the destination for the message 364 * @param userData the data for the message 365 * @param statusReportRequested Indicates whether a report is requested for this message. 366 * @return a <code>SubmitPdu</code> containing the encoded SC 367 * address, if applicable, and the encoded message. 368 * Returns null on encode error. 369 */ 370 public static SubmitPdu getSubmitPdu(String destAddr, UserData userData, 371 boolean statusReportRequested) { 372 return privateGetSubmitPdu(destAddr, statusReportRequested, userData); 373 } 374 375 /** 376 * Note: This function is a GSM specific functionality which is not supported in CDMA mode. 377 */ 378 @Override 379 public int getProtocolIdentifier() { 380 Rlog.w(LOG_TAG, "getProtocolIdentifier: is not supported in CDMA mode."); 381 // (3GPP TS 23.040): "no interworking, but SME to SME protocol": 382 return 0; 383 } 384 385 /** 386 * Note: This function is a GSM specific functionality which is not supported in CDMA mode. 387 */ 388 @Override 389 public boolean isReplace() { 390 Rlog.w(LOG_TAG, "isReplace: is not supported in CDMA mode."); 391 return false; 392 } 393 394 /** 395 * {@inheritDoc} 396 * Note: This function is a GSM specific functionality which is not supported in CDMA mode. 397 */ 398 @Override 399 public boolean isCphsMwiMessage() { 400 Rlog.w(LOG_TAG, "isCphsMwiMessage: is not supported in CDMA mode."); 401 return false; 402 } 403 404 /** 405 * {@inheritDoc} 406 */ 407 @Override 408 public boolean isMWIClearMessage() { 409 return ((mBearerData != null) && (mBearerData.numberOfMessages == 0)); 410 } 411 412 /** 413 * {@inheritDoc} 414 */ 415 @Override 416 public boolean isMWISetMessage() { 417 return ((mBearerData != null) && (mBearerData.numberOfMessages > 0)); 418 } 419 420 /** 421 * {@inheritDoc} 422 */ 423 @Override 424 public boolean isMwiDontStore() { 425 return ((mBearerData != null) && 426 (mBearerData.numberOfMessages > 0) && 427 (mBearerData.userData == null)); 428 } 429 430 /** 431 * Returns the status for a previously submitted message. 432 * For not interfering with status codes from GSM, this status code is 433 * shifted to the bits 31-16. 434 */ 435 @Override 436 public int getStatus() { 437 return (status << 16); 438 } 439 440 /** Return true iff the bearer data message type is DELIVERY_ACK. */ 441 @Override 442 public boolean isStatusReportMessage() { 443 return (mBearerData.messageType == BearerData.MESSAGE_TYPE_DELIVERY_ACK); 444 } 445 446 /** 447 * Note: This function is a GSM specific functionality which is not supported in CDMA mode. 448 */ 449 @Override 450 public boolean isReplyPathPresent() { 451 Rlog.w(LOG_TAG, "isReplyPathPresent: is not supported in CDMA mode."); 452 return false; 453 } 454 455 /** 456 * Calculate the number of septets needed to encode the message. 457 * 458 * @param messageBody the message to encode 459 * @param use7bitOnly ignore (but still count) illegal characters if true 460 * @return TextEncodingDetails 461 */ 462 public static TextEncodingDetails calculateLength(CharSequence messageBody, 463 boolean use7bitOnly) { 464 CharSequence newMsgBody = null; 465 Resources r = Resources.getSystem(); 466 if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) { 467 newMsgBody = Sms7BitEncodingTranslator.translate(messageBody); 468 } 469 if (TextUtils.isEmpty(newMsgBody)) { 470 newMsgBody = messageBody; 471 } 472 return BearerData.calcTextEncodingDetails(newMsgBody, use7bitOnly); 473 } 474 475 /** 476 * Returns the teleservice type of the message. 477 * @return the teleservice: 478 * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_NOT_SET}, 479 * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WMT}, 480 * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WEMT}, 481 * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_VMN}, 482 * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WAP} 483 */ 484 /* package */ int getTeleService() { 485 return mEnvelope.teleService; 486 } 487 488 /** 489 * Returns the message type of the message. 490 * @return the message type: 491 * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_POINT_TO_POINT}, 492 * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_BROADCAST}, 493 * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_ACKNOWLEDGE}, 494 */ 495 /* package */ int getMessageType() { 496 // NOTE: mEnvelope.messageType is not set correctly for cell broadcasts with some RILs. 497 // Use the service category parameter to detect CMAS and other cell broadcast messages. 498 if (mEnvelope.serviceCategory != 0) { 499 return SmsEnvelope.MESSAGE_TYPE_BROADCAST; 500 } else { 501 return SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT; 502 } 503 } 504 505 /** 506 * Decodes pdu to an empty SMS object. 507 * In the CDMA case the pdu is just an internal byte stream representation 508 * of the SMS Java-object. 509 * @see #createPdu() 510 */ 511 private void parsePdu(byte[] pdu) { 512 ByteArrayInputStream bais = new ByteArrayInputStream(pdu); 513 DataInputStream dis = new DataInputStream(bais); 514 int length; 515 int bearerDataLength; 516 SmsEnvelope env = new SmsEnvelope(); 517 CdmaSmsAddress addr = new CdmaSmsAddress(); 518 519 try { 520 env.messageType = dis.readInt(); 521 env.teleService = dis.readInt(); 522 env.serviceCategory = dis.readInt(); 523 524 addr.digitMode = dis.readByte(); 525 addr.numberMode = dis.readByte(); 526 addr.ton = dis.readByte(); 527 addr.numberPlan = dis.readByte(); 528 529 length = dis.readUnsignedByte(); 530 addr.numberOfDigits = length; 531 532 // sanity check on the length 533 if (length > pdu.length) { 534 throw new RuntimeException( 535 "createFromPdu: Invalid pdu, addr.numberOfDigits " + length 536 + " > pdu len " + pdu.length); 537 } 538 addr.origBytes = new byte[length]; 539 dis.read(addr.origBytes, 0, length); // digits 540 541 env.bearerReply = dis.readInt(); 542 // CauseCode values: 543 env.replySeqNo = dis.readByte(); 544 env.errorClass = dis.readByte(); 545 env.causeCode = dis.readByte(); 546 547 //encoded BearerData: 548 bearerDataLength = dis.readInt(); 549 // sanity check on the length 550 if (bearerDataLength > pdu.length) { 551 throw new RuntimeException( 552 "createFromPdu: Invalid pdu, bearerDataLength " + bearerDataLength 553 + " > pdu len " + pdu.length); 554 } 555 env.bearerData = new byte[bearerDataLength]; 556 dis.read(env.bearerData, 0, bearerDataLength); 557 dis.close(); 558 } catch (IOException ex) { 559 throw new RuntimeException( 560 "createFromPdu: conversion from byte array to object failed: " + ex, ex); 561 } catch (Exception ex) { 562 Rlog.e(LOG_TAG, "createFromPdu: conversion from byte array to object failed: " + ex); 563 } 564 565 // link the filled objects to this SMS 566 mOriginatingAddress = addr; 567 env.origAddress = addr; 568 mEnvelope = env; 569 mPdu = pdu; 570 571 parseSms(); 572 } 573 574 /** 575 * Decodes 3GPP2 sms stored in CSIM/RUIM cards As per 3GPP2 C.S0015-0 576 */ 577 private void parsePduFromEfRecord(byte[] pdu) { 578 ByteArrayInputStream bais = new ByteArrayInputStream(pdu); 579 DataInputStream dis = new DataInputStream(bais); 580 SmsEnvelope env = new SmsEnvelope(); 581 CdmaSmsAddress addr = new CdmaSmsAddress(); 582 CdmaSmsSubaddress subAddr = new CdmaSmsSubaddress(); 583 584 try { 585 env.messageType = dis.readByte(); 586 587 while (dis.available() > 0) { 588 int parameterId = dis.readByte(); 589 int parameterLen = dis.readUnsignedByte(); 590 byte[] parameterData = new byte[parameterLen]; 591 592 switch (parameterId) { 593 case TELESERVICE_IDENTIFIER: 594 /* 595 * 16 bit parameter that identifies which upper layer 596 * service access point is sending or should receive 597 * this message 598 */ 599 env.teleService = dis.readUnsignedShort(); 600 Rlog.i(LOG_TAG, "teleservice = " + env.teleService); 601 break; 602 case SERVICE_CATEGORY: 603 /* 604 * 16 bit parameter that identifies type of service as 605 * in 3GPP2 C.S0015-0 Table 3.4.3.2-1 606 */ 607 env.serviceCategory = dis.readUnsignedShort(); 608 break; 609 case ORIGINATING_ADDRESS: 610 case DESTINATION_ADDRESS: 611 dis.read(parameterData, 0, parameterLen); 612 BitwiseInputStream addrBis = new BitwiseInputStream(parameterData); 613 addr.digitMode = addrBis.read(1); 614 addr.numberMode = addrBis.read(1); 615 int numberType = 0; 616 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 617 numberType = addrBis.read(3); 618 addr.ton = numberType; 619 620 if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK) 621 addr.numberPlan = addrBis.read(4); 622 } 623 624 addr.numberOfDigits = addrBis.read(8); 625 626 byte[] data = new byte[addr.numberOfDigits]; 627 byte b = 0x00; 628 629 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF) { 630 /* As per 3GPP2 C.S0005-0 Table 2.7.1.3.2.4-4 */ 631 for (int index = 0; index < addr.numberOfDigits; index++) { 632 b = (byte) (0xF & addrBis.read(4)); 633 // convert the value if it is 4-bit DTMF to 8 634 // bit 635 data[index] = convertDtmfToAscii(b); 636 } 637 } else if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 638 if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK) { 639 for (int index = 0; index < addr.numberOfDigits; index++) { 640 b = (byte) (0xFF & addrBis.read(8)); 641 data[index] = b; 642 } 643 644 } else if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK) { 645 if (numberType == 2) 646 Rlog.e(LOG_TAG, "TODO: Originating Addr is email id"); 647 else 648 Rlog.e(LOG_TAG, 649 "TODO: Originating Addr is data network address"); 650 } else { 651 Rlog.e(LOG_TAG, "Originating Addr is of incorrect type"); 652 } 653 } else { 654 Rlog.e(LOG_TAG, "Incorrect Digit mode"); 655 } 656 addr.origBytes = data; 657 Rlog.i(LOG_TAG, "Originating Addr=" + addr.toString()); 658 break; 659 case ORIGINATING_SUB_ADDRESS: 660 case DESTINATION_SUB_ADDRESS: 661 dis.read(parameterData, 0, parameterLen); 662 BitwiseInputStream subAddrBis = new BitwiseInputStream(parameterData); 663 subAddr.type = subAddrBis.read(3); 664 subAddr.odd = subAddrBis.readByteArray(1)[0]; 665 int subAddrLen = subAddrBis.read(8); 666 byte[] subdata = new byte[subAddrLen]; 667 for (int index = 0; index < subAddrLen; index++) { 668 b = (byte) (0xFF & subAddrBis.read(4)); 669 // convert the value if it is 4-bit DTMF to 8 bit 670 subdata[index] = convertDtmfToAscii(b); 671 } 672 subAddr.origBytes = subdata; 673 break; 674 case BEARER_REPLY_OPTION: 675 dis.read(parameterData, 0, parameterLen); 676 BitwiseInputStream replyOptBis = new BitwiseInputStream(parameterData); 677 env.bearerReply = replyOptBis.read(6); 678 break; 679 case CAUSE_CODES: 680 dis.read(parameterData, 0, parameterLen); 681 BitwiseInputStream ccBis = new BitwiseInputStream(parameterData); 682 env.replySeqNo = ccBis.readByteArray(6)[0]; 683 env.errorClass = ccBis.readByteArray(2)[0]; 684 if (env.errorClass != 0x00) 685 env.causeCode = ccBis.readByteArray(8)[0]; 686 break; 687 case BEARER_DATA: 688 dis.read(parameterData, 0, parameterLen); 689 env.bearerData = parameterData; 690 break; 691 default: 692 throw new Exception("unsupported parameterId (" + parameterId + ")"); 693 } 694 } 695 bais.close(); 696 dis.close(); 697 } catch (Exception ex) { 698 Rlog.e(LOG_TAG, "parsePduFromEfRecord: conversion from pdu to SmsMessage failed" + ex); 699 } 700 701 // link the filled objects to this SMS 702 mOriginatingAddress = addr; 703 env.origAddress = addr; 704 env.origSubaddress = subAddr; 705 mEnvelope = env; 706 mPdu = pdu; 707 708 parseSms(); 709 } 710 711 /** 712 * Parses a SMS message from its BearerData stream. (mobile-terminated only) 713 */ 714 protected void parseSms() { 715 // Message Waiting Info Record defined in 3GPP2 C.S-0005, 3.7.5.6 716 // It contains only an 8-bit number with the number of messages waiting 717 if (mEnvelope.teleService == SmsEnvelope.TELESERVICE_MWI) { 718 mBearerData = new BearerData(); 719 if (mEnvelope.bearerData != null) { 720 mBearerData.numberOfMessages = 0x000000FF & mEnvelope.bearerData[0]; 721 } 722 if (VDBG) { 723 Rlog.d(LOG_TAG, "parseSms: get MWI " + 724 Integer.toString(mBearerData.numberOfMessages)); 725 } 726 return; 727 } 728 mBearerData = BearerData.decode(mEnvelope.bearerData); 729 if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) { 730 Rlog.d(LOG_TAG, "MT raw BearerData = '" + 731 HexDump.toHexString(mEnvelope.bearerData) + "'"); 732 Rlog.d(LOG_TAG, "MT (decoded) BearerData = " + mBearerData); 733 } 734 mMessageRef = mBearerData.messageId; 735 if (mBearerData.userData != null) { 736 mUserData = mBearerData.userData.payload; 737 mUserDataHeader = mBearerData.userData.userDataHeader; 738 mMessageBody = mBearerData.userData.payloadStr; 739 } 740 741 if (mOriginatingAddress != null) { 742 mOriginatingAddress.address = new String(mOriginatingAddress.origBytes); 743 if (mOriginatingAddress.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) { 744 if (mOriginatingAddress.address.charAt(0) != '+') { 745 mOriginatingAddress.address = "+" + mOriginatingAddress.address; 746 } 747 } 748 if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: " 749 + mOriginatingAddress.address); 750 } 751 752 if (mBearerData.msgCenterTimeStamp != null) { 753 mScTimeMillis = mBearerData.msgCenterTimeStamp.toMillis(true); 754 } 755 756 if (VDBG) Rlog.d(LOG_TAG, "SMS SC timestamp: " + mScTimeMillis); 757 758 // Message Type (See 3GPP2 C.S0015-B, v2, 4.5.1) 759 if (mBearerData.messageType == BearerData.MESSAGE_TYPE_DELIVERY_ACK) { 760 // The BearerData MsgStatus subparameter should only be 761 // included for DELIVERY_ACK messages. If it occurred for 762 // other messages, it would be unclear what the status 763 // being reported refers to. The MsgStatus subparameter 764 // is primarily useful to indicate error conditions -- a 765 // message without this subparameter is assumed to 766 // indicate successful delivery (status == 0). 767 if (! mBearerData.messageStatusSet) { 768 Rlog.d(LOG_TAG, "DELIVERY_ACK message without msgStatus (" + 769 (mUserData == null ? "also missing" : "does have") + 770 " userData)."); 771 status = 0; 772 } else { 773 status = mBearerData.errorClass << 8; 774 status |= mBearerData.messageStatus; 775 } 776 } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER) { 777 throw new RuntimeException("Unsupported message type: " + mBearerData.messageType); 778 } 779 780 if (mMessageBody != null) { 781 if (VDBG) Rlog.v(LOG_TAG, "SMS message body: '" + mMessageBody + "'"); 782 parseMessageBody(); 783 } else if ((mUserData != null) && VDBG) { 784 Rlog.v(LOG_TAG, "SMS payload: '" + IccUtils.bytesToHexString(mUserData) + "'"); 785 } 786 } 787 788 /** 789 * Parses a broadcast SMS, possibly containing a CMAS alert. 790 */ 791 SmsCbMessage parseBroadcastSms() { 792 BearerData bData = BearerData.decode(mEnvelope.bearerData, mEnvelope.serviceCategory); 793 if (bData == null) { 794 Rlog.w(LOG_TAG, "BearerData.decode() returned null"); 795 return null; 796 } 797 798 if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) { 799 Rlog.d(LOG_TAG, "MT raw BearerData = " + HexDump.toHexString(mEnvelope.bearerData)); 800 } 801 802 String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC); 803 SmsCbLocation location = new SmsCbLocation(plmn); 804 805 return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP2, 806 SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE, bData.messageId, location, 807 mEnvelope.serviceCategory, bData.getLanguage(), bData.userData.payloadStr, 808 bData.priority, null, bData.cmasWarningInfo); 809 } 810 811 /** 812 * {@inheritDoc} 813 */ 814 @Override 815 public SmsConstants.MessageClass getMessageClass() { 816 if (BearerData.DISPLAY_MODE_IMMEDIATE == mBearerData.displayMode ) { 817 return SmsConstants.MessageClass.CLASS_0; 818 } else { 819 return SmsConstants.MessageClass.UNKNOWN; 820 } 821 } 822 823 /** 824 * Calculate the next message id, starting at 1 and iteratively 825 * incrementing within the range 1..65535 remembering the state 826 * via a persistent system property. (See C.S0015-B, v2.0, 827 * 4.3.1.5) Since this routine is expected to be accessed via via 828 * binder-call, and hence should be thread-safe, it has been 829 * synchronized. 830 */ 831 synchronized static int getNextMessageId() { 832 // Testing and dialog with partners has indicated that 833 // msgId==0 is (sometimes?) treated specially by lower levels. 834 // Specifically, the ID is not preserved for delivery ACKs. 835 // Hence, avoid 0 -- constraining the range to 1..65535. 836 int msgId = SystemProperties.getInt(TelephonyProperties.PROPERTY_CDMA_MSG_ID, 1); 837 String nextMsgId = Integer.toString((msgId % 0xFFFF) + 1); 838 SystemProperties.set(TelephonyProperties.PROPERTY_CDMA_MSG_ID, nextMsgId); 839 if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) { 840 Rlog.d(LOG_TAG, "next " + TelephonyProperties.PROPERTY_CDMA_MSG_ID + " = " + nextMsgId); 841 Rlog.d(LOG_TAG, "readback gets " + 842 SystemProperties.get(TelephonyProperties.PROPERTY_CDMA_MSG_ID)); 843 } 844 return msgId; 845 } 846 847 /** 848 * Creates BearerData and Envelope from parameters for a Submit SMS. 849 * @return byte stream for SubmitPdu. 850 */ 851 private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested, 852 UserData userData) { 853 854 /** 855 * TODO(cleanup): give this function a more meaningful name. 856 */ 857 858 /** 859 * TODO(cleanup): Make returning null from the getSubmitPdu 860 * variations meaningful -- clean up the error feedback 861 * mechanism, and avoid null pointer exceptions. 862 */ 863 864 /** 865 * North America Plus Code : 866 * Convert + code to 011 and dial out for international SMS 867 */ 868 CdmaSmsAddress destAddr = CdmaSmsAddress.parse( 869 PhoneNumberUtils.cdmaCheckAndProcessPlusCodeForSms(destAddrStr)); 870 if (destAddr == null) return null; 871 872 BearerData bearerData = new BearerData(); 873 bearerData.messageType = BearerData.MESSAGE_TYPE_SUBMIT; 874 875 bearerData.messageId = getNextMessageId(); 876 877 bearerData.deliveryAckReq = statusReportRequested; 878 bearerData.userAckReq = false; 879 bearerData.readAckReq = false; 880 bearerData.reportReq = false; 881 882 bearerData.userData = userData; 883 884 byte[] encodedBearerData = BearerData.encode(bearerData); 885 if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) { 886 Rlog.d(LOG_TAG, "MO (encoded) BearerData = " + bearerData); 887 Rlog.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'"); 888 } 889 if (encodedBearerData == null) return null; 890 891 int teleservice = bearerData.hasUserDataHeader ? 892 SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT; 893 894 SmsEnvelope envelope = new SmsEnvelope(); 895 envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT; 896 envelope.teleService = teleservice; 897 envelope.destAddress = destAddr; 898 envelope.bearerReply = RETURN_ACK; 899 envelope.bearerData = encodedBearerData; 900 901 /** 902 * TODO(cleanup): envelope looks to be a pointless class, get 903 * rid of it. Also -- most of the envelope fields set here 904 * are ignored, why? 905 */ 906 907 try { 908 /** 909 * TODO(cleanup): reference a spec and get rid of the ugly comments 910 */ 911 ByteArrayOutputStream baos = new ByteArrayOutputStream(100); 912 DataOutputStream dos = new DataOutputStream(baos); 913 dos.writeInt(envelope.teleService); 914 dos.writeInt(0); //servicePresent 915 dos.writeInt(0); //serviceCategory 916 dos.write(destAddr.digitMode); 917 dos.write(destAddr.numberMode); 918 dos.write(destAddr.ton); // number_type 919 dos.write(destAddr.numberPlan); 920 dos.write(destAddr.numberOfDigits); 921 dos.write(destAddr.origBytes, 0, destAddr.origBytes.length); // digits 922 // Subaddress is not supported. 923 dos.write(0); //subaddressType 924 dos.write(0); //subaddr_odd 925 dos.write(0); //subaddr_nbr_of_digits 926 dos.write(encodedBearerData.length); 927 dos.write(encodedBearerData, 0, encodedBearerData.length); 928 dos.close(); 929 930 SubmitPdu pdu = new SubmitPdu(); 931 pdu.encodedMessage = baos.toByteArray(); 932 pdu.encodedScAddress = null; 933 return pdu; 934 } catch(IOException ex) { 935 Rlog.e(LOG_TAG, "creating SubmitPdu failed: " + ex); 936 } 937 return null; 938 } 939 940 /** 941 * Creates byte array (pseudo pdu) from SMS object. 942 * Note: Do not call this method more than once per object! 943 */ 944 private void createPdu() { 945 SmsEnvelope env = mEnvelope; 946 CdmaSmsAddress addr = env.origAddress; 947 ByteArrayOutputStream baos = new ByteArrayOutputStream(100); 948 DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos)); 949 950 try { 951 dos.writeInt(env.messageType); 952 dos.writeInt(env.teleService); 953 dos.writeInt(env.serviceCategory); 954 955 dos.writeByte(addr.digitMode); 956 dos.writeByte(addr.numberMode); 957 dos.writeByte(addr.ton); 958 dos.writeByte(addr.numberPlan); 959 dos.writeByte(addr.numberOfDigits); 960 dos.write(addr.origBytes, 0, addr.origBytes.length); // digits 961 962 dos.writeInt(env.bearerReply); 963 // CauseCode values: 964 dos.writeByte(env.replySeqNo); 965 dos.writeByte(env.errorClass); 966 dos.writeByte(env.causeCode); 967 //encoded BearerData: 968 dos.writeInt(env.bearerData.length); 969 dos.write(env.bearerData, 0, env.bearerData.length); 970 dos.close(); 971 972 /** 973 * TODO(cleanup) -- The mPdu field is managed in 974 * a fragile manner, and it would be much nicer if 975 * accessing the serialized representation used a less 976 * fragile mechanism. Maybe the getPdu method could 977 * generate a representation if there was not yet one? 978 */ 979 980 mPdu = baos.toByteArray(); 981 } catch (IOException ex) { 982 Rlog.e(LOG_TAG, "createPdu: conversion from object to byte array failed: " + ex); 983 } 984 } 985 986 /** 987 * Converts a 4-Bit DTMF encoded symbol from the calling address number to ASCII character 988 */ 989 private byte convertDtmfToAscii(byte dtmfDigit) { 990 byte asciiDigit; 991 992 switch (dtmfDigit) { 993 case 0: asciiDigit = 68; break; // 'D' 994 case 1: asciiDigit = 49; break; // '1' 995 case 2: asciiDigit = 50; break; // '2' 996 case 3: asciiDigit = 51; break; // '3' 997 case 4: asciiDigit = 52; break; // '4' 998 case 5: asciiDigit = 53; break; // '5' 999 case 6: asciiDigit = 54; break; // '6' 1000 case 7: asciiDigit = 55; break; // '7' 1001 case 8: asciiDigit = 56; break; // '8' 1002 case 9: asciiDigit = 57; break; // '9' 1003 case 10: asciiDigit = 48; break; // '0' 1004 case 11: asciiDigit = 42; break; // '*' 1005 case 12: asciiDigit = 35; break; // '#' 1006 case 13: asciiDigit = 65; break; // 'A' 1007 case 14: asciiDigit = 66; break; // 'B' 1008 case 15: asciiDigit = 67; break; // 'C' 1009 default: 1010 asciiDigit = 32; // Invalid DTMF code 1011 break; 1012 } 1013 1014 return asciiDigit; 1015 } 1016 1017 /** This function shall be called to get the number of voicemails. 1018 * @hide 1019 */ 1020 /*package*/ int getNumOfVoicemails() { 1021 return mBearerData.numberOfMessages; 1022 } 1023 1024 /** 1025 * Returns a byte array that can be use to uniquely identify a received SMS message. 1026 * C.S0015-B 4.3.1.6 Unique Message Identification. 1027 * 1028 * @return byte array uniquely identifying the message. 1029 * @hide 1030 */ 1031 /* package */ byte[] getIncomingSmsFingerprint() { 1032 ByteArrayOutputStream output = new ByteArrayOutputStream(); 1033 1034 output.write(mEnvelope.serviceCategory); 1035 output.write(mEnvelope.teleService); 1036 output.write(mEnvelope.origAddress.origBytes, 0, mEnvelope.origAddress.origBytes.length); 1037 output.write(mEnvelope.bearerData, 0, mEnvelope.bearerData.length); 1038 output.write(mEnvelope.origSubaddress.origBytes, 0, 1039 mEnvelope.origSubaddress.origBytes.length); 1040 1041 return output.toByteArray(); 1042 } 1043 1044 /** 1045 * Returns the list of service category program data, if present. 1046 * @return a list of CdmaSmsCbProgramData objects, or null if not present 1047 * @hide 1048 */ 1049 public ArrayList<CdmaSmsCbProgramData> getSmsCbProgramData() { 1050 return mBearerData.serviceCategoryProgramData; 1051 } 1052} 1053