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