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