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