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