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