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