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.sms; 18 19import static android.telephony.SmsMessage.ENCODING_16BIT; 20import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES; 21import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER; 22 23import android.util.Log; 24 25import android.telephony.SmsMessage; 26 27import android.text.format.Time; 28 29import com.android.internal.telephony.IccUtils; 30import com.android.internal.telephony.GsmAlphabet; 31import com.android.internal.telephony.SmsHeader; 32import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; 33 34import com.android.internal.util.BitwiseInputStream; 35import com.android.internal.util.BitwiseOutputStream; 36 37import android.content.res.Resources; 38 39 40 41/** 42 * An object to encode and decode CDMA SMS bearer data. 43 */ 44public final class BearerData { 45 private final static String LOG_TAG = "SMS"; 46 47 /** 48 * Bearer Data Subparameter Indentifiers 49 * (See 3GPP2 C.S0015-B, v2.0, table 4.5-1) 50 * NOTE: Commented subparameter types are not implemented. 51 */ 52 private final static byte SUBPARAM_MESSAGE_IDENTIFIER = 0x00; 53 private final static byte SUBPARAM_USER_DATA = 0x01; 54 private final static byte SUBPARAM_USER_REPONSE_CODE = 0x02; 55 private final static byte SUBPARAM_MESSAGE_CENTER_TIME_STAMP = 0x03; 56 private final static byte SUBPARAM_VALIDITY_PERIOD_ABSOLUTE = 0x04; 57 private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE = 0x05; 58 private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE = 0x06; 59 private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE = 0x07; 60 private final static byte SUBPARAM_PRIORITY_INDICATOR = 0x08; 61 private final static byte SUBPARAM_PRIVACY_INDICATOR = 0x09; 62 private final static byte SUBPARAM_REPLY_OPTION = 0x0A; 63 private final static byte SUBPARAM_NUMBER_OF_MESSAGES = 0x0B; 64 private final static byte SUBPARAM_ALERT_ON_MESSAGE_DELIVERY = 0x0C; 65 private final static byte SUBPARAM_LANGUAGE_INDICATOR = 0x0D; 66 private final static byte SUBPARAM_CALLBACK_NUMBER = 0x0E; 67 private final static byte SUBPARAM_MESSAGE_DISPLAY_MODE = 0x0F; 68 //private final static byte SUBPARAM_MULTIPLE_ENCODING_USER_DATA = 0x10; 69 private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX = 0x11; 70 //private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA = 0x12; 71 //private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS = 0x13; 72 private final static byte SUBPARAM_MESSAGE_STATUS = 0x14; 73 //private final static byte SUBPARAM_TP_FAILURE_CAUSE = 0x15; 74 //private final static byte SUBPARAM_ENHANCED_VMN = 0x16; 75 //private final static byte SUBPARAM_ENHANCED_VMN_ACK = 0x17; 76 77 /** 78 * Supported message types for CDMA SMS messages 79 * (See 3GPP2 C.S0015-B, v2.0, table 4.5.1-1) 80 */ 81 public static final int MESSAGE_TYPE_DELIVER = 0x01; 82 public static final int MESSAGE_TYPE_SUBMIT = 0x02; 83 public static final int MESSAGE_TYPE_CANCELLATION = 0x03; 84 public static final int MESSAGE_TYPE_DELIVERY_ACK = 0x04; 85 public static final int MESSAGE_TYPE_USER_ACK = 0x05; 86 public static final int MESSAGE_TYPE_READ_ACK = 0x06; 87 public static final int MESSAGE_TYPE_DELIVER_REPORT = 0x07; 88 public static final int MESSAGE_TYPE_SUBMIT_REPORT = 0x08; 89 90 public int messageType; 91 92 /** 93 * 16-bit value indicating the message ID, which increments modulo 65536. 94 * (Special rules apply for WAP-messages.) 95 * (See 3GPP2 C.S0015-B, v2, 4.5.1) 96 */ 97 public int messageId; 98 99 /** 100 * Supported priority modes for CDMA SMS messages 101 * (See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1) 102 */ 103 public static final int PRIORITY_NORMAL = 0x0; 104 public static final int PRIORITY_INTERACTIVE = 0x1; 105 public static final int PRIORITY_URGENT = 0x2; 106 public static final int PRIORITY_EMERGENCY = 0x3; 107 108 public boolean priorityIndicatorSet = false; 109 public int priority = PRIORITY_NORMAL; 110 111 /** 112 * Supported privacy modes for CDMA SMS messages 113 * (See 3GPP2 C.S0015-B, v2.0, table 4.5.10-1) 114 */ 115 public static final int PRIVACY_NOT_RESTRICTED = 0x0; 116 public static final int PRIVACY_RESTRICTED = 0x1; 117 public static final int PRIVACY_CONFIDENTIAL = 0x2; 118 public static final int PRIVACY_SECRET = 0x3; 119 120 public boolean privacyIndicatorSet = false; 121 public int privacy = PRIVACY_NOT_RESTRICTED; 122 123 /** 124 * Supported alert priority modes for CDMA SMS messages 125 * (See 3GPP2 C.S0015-B, v2.0, table 4.5.13-1) 126 */ 127 public static final int ALERT_DEFAULT = 0x0; 128 public static final int ALERT_LOW_PRIO = 0x1; 129 public static final int ALERT_MEDIUM_PRIO = 0x2; 130 public static final int ALERT_HIGH_PRIO = 0x3; 131 132 public boolean alertIndicatorSet = false; 133 public int alert = ALERT_DEFAULT; 134 135 /** 136 * Supported display modes for CDMA SMS messages. Display mode is 137 * a 2-bit value used to indicate to the mobile station when to 138 * display the received message. (See 3GPP2 C.S0015-B, v2, 139 * 4.5.16) 140 */ 141 public static final int DISPLAY_MODE_IMMEDIATE = 0x0; 142 public static final int DISPLAY_MODE_DEFAULT = 0x1; 143 public static final int DISPLAY_MODE_USER = 0x2; 144 145 public boolean displayModeSet = false; 146 public int displayMode = DISPLAY_MODE_DEFAULT; 147 148 /** 149 * Language Indicator values. NOTE: the spec (3GPP2 C.S0015-B, 150 * v2, 4.5.14) is ambiguous as to the meaning of this field, as it 151 * refers to C.R1001-D but that reference has been crossed out. 152 * It would seem reasonable to assume the values from C.R1001-F 153 * (table 9.2-1) are to be used instead. 154 */ 155 public static final int LANGUAGE_UNKNOWN = 0x00; 156 public static final int LANGUAGE_ENGLISH = 0x01; 157 public static final int LANGUAGE_FRENCH = 0x02; 158 public static final int LANGUAGE_SPANISH = 0x03; 159 public static final int LANGUAGE_JAPANESE = 0x04; 160 public static final int LANGUAGE_KOREAN = 0x05; 161 public static final int LANGUAGE_CHINESE = 0x06; 162 public static final int LANGUAGE_HEBREW = 0x07; 163 164 public boolean languageIndicatorSet = false; 165 public int language = LANGUAGE_UNKNOWN; 166 167 /** 168 * SMS Message Status Codes. The first component of the Message 169 * status indicates if an error has occurred and whether the error 170 * is considered permanent or temporary. The second component of 171 * the Message status indicates the cause of the error (if any). 172 * (See 3GPP2 C.S0015-B, v2.0, 4.5.21) 173 */ 174 /* no-error codes */ 175 public static final int ERROR_NONE = 0x00; 176 public static final int STATUS_ACCEPTED = 0x00; 177 public static final int STATUS_DEPOSITED_TO_INTERNET = 0x01; 178 public static final int STATUS_DELIVERED = 0x02; 179 public static final int STATUS_CANCELLED = 0x03; 180 /* temporary-error and permanent-error codes */ 181 public static final int ERROR_TEMPORARY = 0x02; 182 public static final int STATUS_NETWORK_CONGESTION = 0x04; 183 public static final int STATUS_NETWORK_ERROR = 0x05; 184 public static final int STATUS_UNKNOWN_ERROR = 0x1F; 185 /* permanent-error codes */ 186 public static final int ERROR_PERMANENT = 0x03; 187 public static final int STATUS_CANCEL_FAILED = 0x06; 188 public static final int STATUS_BLOCKED_DESTINATION = 0x07; 189 public static final int STATUS_TEXT_TOO_LONG = 0x08; 190 public static final int STATUS_DUPLICATE_MESSAGE = 0x09; 191 public static final int STATUS_INVALID_DESTINATION = 0x0A; 192 public static final int STATUS_MESSAGE_EXPIRED = 0x0D; 193 /* undefined-status codes */ 194 public static final int ERROR_UNDEFINED = 0xFF; 195 public static final int STATUS_UNDEFINED = 0xFF; 196 197 public boolean messageStatusSet = false; 198 public int errorClass = ERROR_UNDEFINED; 199 public int messageStatus = STATUS_UNDEFINED; 200 201 /** 202 * 1-bit value that indicates whether a User Data Header (UDH) is present. 203 * (See 3GPP2 C.S0015-B, v2, 4.5.1) 204 * 205 * NOTE: during encoding, this value will be set based on the 206 * presence of a UDH in the structured data, any existing setting 207 * will be overwritten. 208 */ 209 public boolean hasUserDataHeader; 210 211 /** 212 * provides the information for the user data 213 * (e.g. padding bits, user data, user data header, etc) 214 * (See 3GPP2 C.S.0015-B, v2, 4.5.2) 215 */ 216 public UserData userData; 217 218 /** 219 * The User Response Code subparameter is used in the SMS User 220 * Acknowledgment Message to respond to previously received short 221 * messages. This message center-specific element carries the 222 * identifier of a predefined response. (See 3GPP2 C.S.0015-B, v2, 223 * 4.5.3) 224 */ 225 public boolean userResponseCodeSet = false; 226 public int userResponseCode; 227 228 /** 229 * 6-byte-field, see 3GPP2 C.S0015-B, v2, 4.5.4 230 */ 231 public static class TimeStamp extends Time { 232 233 public TimeStamp() { 234 super(Time.TIMEZONE_UTC); 235 } 236 237 public static TimeStamp fromByteArray(byte[] data) { 238 TimeStamp ts = new TimeStamp(); 239 // C.S0015-B v2.0, 4.5.4: range is 1996-2095 240 int year = IccUtils.cdmaBcdByteToInt(data[0]); 241 if (year > 99 || year < 0) return null; 242 ts.year = year >= 96 ? year + 1900 : year + 2000; 243 int month = IccUtils.cdmaBcdByteToInt(data[1]); 244 if (month < 1 || month > 12) return null; 245 ts.month = month - 1; 246 int day = IccUtils.cdmaBcdByteToInt(data[2]); 247 if (day < 1 || day > 31) return null; 248 ts.monthDay = day; 249 int hour = IccUtils.cdmaBcdByteToInt(data[3]); 250 if (hour < 0 || hour > 23) return null; 251 ts.hour = hour; 252 int minute = IccUtils.cdmaBcdByteToInt(data[4]); 253 if (minute < 0 || minute > 59) return null; 254 ts.minute = minute; 255 int second = IccUtils.cdmaBcdByteToInt(data[5]); 256 if (second < 0 || second > 59) return null; 257 ts.second = second; 258 return ts; 259 } 260 261 @Override 262 public String toString() { 263 StringBuilder builder = new StringBuilder(); 264 builder.append("TimeStamp "); 265 builder.append("{ year=" + year); 266 builder.append(", month=" + month); 267 builder.append(", day=" + monthDay); 268 builder.append(", hour=" + hour); 269 builder.append(", minute=" + minute); 270 builder.append(", second=" + second); 271 builder.append(" }"); 272 return builder.toString(); 273 } 274 } 275 276 public TimeStamp msgCenterTimeStamp; 277 public TimeStamp validityPeriodAbsolute; 278 public TimeStamp deferredDeliveryTimeAbsolute; 279 280 /** 281 * Relative time is specified as one byte, the value of which 282 * falls into a series of ranges, as specified below. The idea is 283 * that shorter time intervals allow greater precision -- the 284 * value means minutes from zero until the MINS_LIMIT (inclusive), 285 * upon which it means hours until the HOURS_LIMIT, and so 286 * forth. (See 3GPP2 C.S0015-B, v2, 4.5.6-1) 287 */ 288 public static final int RELATIVE_TIME_MINS_LIMIT = 143; 289 public static final int RELATIVE_TIME_HOURS_LIMIT = 167; 290 public static final int RELATIVE_TIME_DAYS_LIMIT = 196; 291 public static final int RELATIVE_TIME_WEEKS_LIMIT = 244; 292 public static final int RELATIVE_TIME_INDEFINITE = 245; 293 public static final int RELATIVE_TIME_NOW = 246; 294 public static final int RELATIVE_TIME_MOBILE_INACTIVE = 247; 295 public static final int RELATIVE_TIME_RESERVED = 248; 296 297 public boolean validityPeriodRelativeSet; 298 public int validityPeriodRelative; 299 public boolean deferredDeliveryTimeRelativeSet; 300 public int deferredDeliveryTimeRelative; 301 302 /** 303 * The Reply Option subparameter contains 1-bit values which 304 * indicate whether SMS acknowledgment is requested or not. (See 305 * 3GPP2 C.S0015-B, v2, 4.5.11) 306 */ 307 public boolean userAckReq; 308 public boolean deliveryAckReq; 309 public boolean readAckReq; 310 public boolean reportReq; 311 312 /** 313 * The Number of Messages subparameter (8-bit value) is a decimal 314 * number in the 0 to 99 range representing the number of messages 315 * stored at the Voice Mail System. This element is used by the 316 * Voice Mail Notification service. (See 3GPP2 C.S0015-B, v2, 317 * 4.5.12) 318 */ 319 public int numberOfMessages; 320 321 /** 322 * The Message Deposit Index subparameter is assigned by the 323 * message center as a unique index to the contents of the User 324 * Data subparameter in each message sent to a particular mobile 325 * station. The mobile station, when replying to a previously 326 * received short message which included a Message Deposit Index 327 * subparameter, may include the Message Deposit Index of the 328 * received message to indicate to the message center that the 329 * original contents of the message are to be included in the 330 * reply. (See 3GPP2 C.S0015-B, v2, 4.5.18) 331 */ 332 public int depositIndex; 333 334 /** 335 * 4-bit or 8-bit value that indicates the number to be dialed in reply to a 336 * received SMS message. 337 * (See 3GPP2 C.S0015-B, v2, 4.5.15) 338 */ 339 public CdmaSmsAddress callbackNumber; 340 341 private static class CodingException extends Exception { 342 public CodingException(String s) { 343 super(s); 344 } 345 } 346 347 @Override 348 public String toString() { 349 StringBuilder builder = new StringBuilder(); 350 builder.append("BearerData "); 351 builder.append("{ messageType=" + messageType); 352 builder.append(", messageId=" + (int)messageId); 353 builder.append(", priority=" + (priorityIndicatorSet ? priority : "unset")); 354 builder.append(", privacy=" + (privacyIndicatorSet ? privacy : "unset")); 355 builder.append(", alert=" + (alertIndicatorSet ? alert : "unset")); 356 builder.append(", displayMode=" + (displayModeSet ? displayMode : "unset")); 357 builder.append(", language=" + (languageIndicatorSet ? language : "unset")); 358 builder.append(", errorClass=" + (messageStatusSet ? errorClass : "unset")); 359 builder.append(", msgStatus=" + (messageStatusSet ? messageStatus : "unset")); 360 builder.append(", msgCenterTimeStamp=" + 361 ((msgCenterTimeStamp != null) ? msgCenterTimeStamp : "unset")); 362 builder.append(", validityPeriodAbsolute=" + 363 ((validityPeriodAbsolute != null) ? validityPeriodAbsolute : "unset")); 364 builder.append(", validityPeriodRelative=" + 365 ((validityPeriodRelativeSet) ? validityPeriodRelative : "unset")); 366 builder.append(", deferredDeliveryTimeAbsolute=" + 367 ((deferredDeliveryTimeAbsolute != null) ? deferredDeliveryTimeAbsolute : "unset")); 368 builder.append(", deferredDeliveryTimeRelative=" + 369 ((deferredDeliveryTimeRelativeSet) ? deferredDeliveryTimeRelative : "unset")); 370 builder.append(", userAckReq=" + userAckReq); 371 builder.append(", deliveryAckReq=" + deliveryAckReq); 372 builder.append(", readAckReq=" + readAckReq); 373 builder.append(", reportReq=" + reportReq); 374 builder.append(", numberOfMessages=" + numberOfMessages); 375 builder.append(", callbackNumber=" + callbackNumber); 376 builder.append(", depositIndex=" + depositIndex); 377 builder.append(", hasUserDataHeader=" + hasUserDataHeader); 378 builder.append(", userData=" + userData); 379 builder.append(" }"); 380 return builder.toString(); 381 } 382 383 private static void encodeMessageId(BearerData bData, BitwiseOutputStream outStream) 384 throws BitwiseOutputStream.AccessException 385 { 386 outStream.write(8, 3); 387 outStream.write(4, bData.messageType); 388 outStream.write(8, bData.messageId >> 8); 389 outStream.write(8, bData.messageId); 390 outStream.write(1, bData.hasUserDataHeader ? 1 : 0); 391 outStream.skip(3); 392 } 393 394 private static int countAsciiSeptets(CharSequence msg, boolean force) { 395 int msgLen = msg.length(); 396 if (force) return msgLen; 397 for (int i = 0; i < msgLen; i++) { 398 if (UserData.charToAscii.get(msg.charAt(i), -1) == -1) { 399 return -1; 400 } 401 } 402 return msgLen; 403 } 404 405 /** 406 * Calculate the message text encoding length, fragmentation, and other details. 407 * 408 * @param msg message text 409 * @param force7BitEncoding ignore (but still count) illegal characters if true 410 * @return septet count, or -1 on failure 411 */ 412 public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg, 413 boolean force7BitEncoding) { 414 TextEncodingDetails ted; 415 int septets = countAsciiSeptets(msg, force7BitEncoding); 416 if (septets != -1 && septets <= SmsMessage.MAX_USER_DATA_SEPTETS) { 417 ted = new TextEncodingDetails(); 418 ted.msgCount = 1; 419 ted.codeUnitCount = septets; 420 ted.codeUnitsRemaining = SmsMessage.MAX_USER_DATA_SEPTETS - septets; 421 ted.codeUnitSize = SmsMessage.ENCODING_7BIT; 422 } else { 423 ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength( 424 msg, force7BitEncoding); 425 if (ted.msgCount == 1 && ted.codeUnitSize == SmsMessage.ENCODING_7BIT) { 426 // We don't support single-segment EMS, so calculate for 16-bit 427 // TODO: Consider supporting single-segment EMS 428 ted.codeUnitCount = msg.length(); 429 int octets = ted.codeUnitCount * 2; 430 if (octets > MAX_USER_DATA_BYTES) { 431 ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) / 432 MAX_USER_DATA_BYTES_WITH_HEADER; 433 ted.codeUnitsRemaining = ((ted.msgCount * 434 MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2; 435 } else { 436 ted.msgCount = 1; 437 ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2; 438 } 439 ted.codeUnitSize = ENCODING_16BIT; 440 } 441 } 442 return ted; 443 } 444 445 private static byte[] encode7bitAscii(String msg, boolean force) 446 throws CodingException 447 { 448 try { 449 BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length()); 450 int msgLen = msg.length(); 451 for (int i = 0; i < msgLen; i++) { 452 int charCode = UserData.charToAscii.get(msg.charAt(i), -1); 453 if (charCode == -1) { 454 if (force) { 455 outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR); 456 } else { 457 throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")"); 458 } 459 } else { 460 outStream.write(7, charCode); 461 } 462 } 463 return outStream.toByteArray(); 464 } catch (BitwiseOutputStream.AccessException ex) { 465 throw new CodingException("7bit ASCII encode failed: " + ex); 466 } 467 } 468 469 private static byte[] encodeUtf16(String msg) 470 throws CodingException 471 { 472 try { 473 return msg.getBytes("utf-16be"); 474 } catch (java.io.UnsupportedEncodingException ex) { 475 throw new CodingException("UTF-16 encode failed: " + ex); 476 } 477 } 478 479 private static class Gsm7bitCodingResult { 480 int septets; 481 byte[] data; 482 } 483 484 private static Gsm7bitCodingResult encode7bitGsm(String msg, int septetOffset, boolean force) 485 throws CodingException 486 { 487 try { 488 /* 489 * TODO(cleanup): It would be nice if GsmAlphabet provided 490 * an option to produce just the data without prepending 491 * the septet count, as this function is really just a 492 * wrapper to strip that off. Not to mention that the 493 * septet count is generally known prior to invocation of 494 * the encoder. Note that it cannot be derived from the 495 * resulting array length, since that cannot distinguish 496 * if the last contains either 1 or 8 valid bits. 497 * 498 * TODO(cleanup): The BitwiseXStreams could also be 499 * extended with byte-wise reversed endianness read/write 500 * routines to allow a corresponding implementation of 501 * stringToGsm7BitPacked, and potentially directly support 502 * access to the main bitwise stream from encode/decode. 503 */ 504 byte[] fullData = GsmAlphabet.stringToGsm7BitPacked(msg, septetOffset, !force, 0, 0); 505 Gsm7bitCodingResult result = new Gsm7bitCodingResult(); 506 result.data = new byte[fullData.length - 1]; 507 System.arraycopy(fullData, 1, result.data, 0, fullData.length - 1); 508 result.septets = fullData[0] & 0x00FF; 509 return result; 510 } catch (com.android.internal.telephony.EncodeException ex) { 511 throw new CodingException("7bit GSM encode failed: " + ex); 512 } 513 } 514 515 private static void encode7bitEms(UserData uData, byte[] udhData, boolean force) 516 throws CodingException 517 { 518 int udhBytes = udhData.length + 1; // Add length octet. 519 int udhSeptets = ((udhBytes * 8) + 6) / 7; 520 Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, udhSeptets, force); 521 uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET; 522 uData.msgEncodingSet = true; 523 uData.numFields = gcr.septets; 524 uData.payload = gcr.data; 525 uData.payload[0] = (byte)udhData.length; 526 System.arraycopy(udhData, 0, uData.payload, 1, udhData.length); 527 } 528 529 private static void encode16bitEms(UserData uData, byte[] udhData) 530 throws CodingException 531 { 532 byte[] payload = encodeUtf16(uData.payloadStr); 533 int udhBytes = udhData.length + 1; // Add length octet. 534 int udhCodeUnits = (udhBytes + 1) / 2; 535 int udhPadding = udhBytes % 2; 536 int payloadCodeUnits = payload.length / 2; 537 uData.msgEncoding = UserData.ENCODING_UNICODE_16; 538 uData.msgEncodingSet = true; 539 uData.numFields = udhCodeUnits + payloadCodeUnits; 540 uData.payload = new byte[uData.numFields * 2]; 541 uData.payload[0] = (byte)udhData.length; 542 System.arraycopy(udhData, 0, uData.payload, 1, udhData.length); 543 System.arraycopy(payload, 0, uData.payload, udhBytes + udhPadding, payload.length); 544 } 545 546 private static void encodeEmsUserDataPayload(UserData uData) 547 throws CodingException 548 { 549 byte[] headerData = SmsHeader.toByteArray(uData.userDataHeader); 550 if (uData.msgEncodingSet) { 551 if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) { 552 encode7bitEms(uData, headerData, true); 553 } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) { 554 encode16bitEms(uData, headerData); 555 } else { 556 throw new CodingException("unsupported EMS user data encoding (" + 557 uData.msgEncoding + ")"); 558 } 559 } else { 560 try { 561 encode7bitEms(uData, headerData, false); 562 } catch (CodingException ex) { 563 encode16bitEms(uData, headerData); 564 } 565 } 566 } 567 568 private static void encodeUserDataPayload(UserData uData) 569 throws CodingException 570 { 571 if ((uData.payloadStr == null) && (uData.msgEncoding != UserData.ENCODING_OCTET)) { 572 Log.e(LOG_TAG, "user data with null payloadStr"); 573 uData.payloadStr = ""; 574 } 575 576 if (uData.userDataHeader != null) { 577 encodeEmsUserDataPayload(uData); 578 return; 579 } 580 581 if (uData.msgEncodingSet) { 582 if (uData.msgEncoding == UserData.ENCODING_OCTET) { 583 if (uData.payload == null) { 584 Log.e(LOG_TAG, "user data with octet encoding but null payload"); 585 uData.payload = new byte[0]; 586 uData.numFields = 0; 587 } else { 588 uData.payload = uData.payload; 589 uData.numFields = uData.payload.length; 590 } 591 } else { 592 if (uData.payloadStr == null) { 593 Log.e(LOG_TAG, "non-octet user data with null payloadStr"); 594 uData.payloadStr = ""; 595 } 596 if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) { 597 Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, 0, true); 598 uData.payload = gcr.data; 599 uData.numFields = gcr.septets; 600 } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) { 601 uData.payload = encode7bitAscii(uData.payloadStr, true); 602 uData.numFields = uData.payloadStr.length(); 603 } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) { 604 uData.payload = encodeUtf16(uData.payloadStr); 605 uData.numFields = uData.payloadStr.length(); 606 } else { 607 throw new CodingException("unsupported user data encoding (" + 608 uData.msgEncoding + ")"); 609 } 610 } 611 } else { 612 try { 613 uData.payload = encode7bitAscii(uData.payloadStr, false); 614 uData.msgEncoding = UserData.ENCODING_7BIT_ASCII; 615 } catch (CodingException ex) { 616 uData.payload = encodeUtf16(uData.payloadStr); 617 uData.msgEncoding = UserData.ENCODING_UNICODE_16; 618 } 619 uData.numFields = uData.payloadStr.length(); 620 uData.msgEncodingSet = true; 621 } 622 } 623 624 private static void encodeUserData(BearerData bData, BitwiseOutputStream outStream) 625 throws BitwiseOutputStream.AccessException, CodingException 626 { 627 /* 628 * TODO(cleanup): Do we really need to set userData.payload as 629 * a side effect of encoding? If not, we could avoid data 630 * copies by passing outStream directly. 631 */ 632 encodeUserDataPayload(bData.userData); 633 bData.hasUserDataHeader = bData.userData.userDataHeader != null; 634 635 if (bData.userData.payload.length > SmsMessage.MAX_USER_DATA_BYTES) { 636 throw new CodingException("encoded user data too large (" + 637 bData.userData.payload.length + 638 " > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)"); 639 } 640 641 /* 642 * TODO(cleanup): figure out what the right answer is WRT paddingBits field 643 * 644 * userData.paddingBits = (userData.payload.length * 8) - (userData.numFields * 7); 645 * userData.paddingBits = 0; // XXX this seems better, but why? 646 * 647 */ 648 int dataBits = (bData.userData.payload.length * 8) - bData.userData.paddingBits; 649 int paramBits = dataBits + 13; 650 if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) || 651 (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) { 652 paramBits += 8; 653 } 654 int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0); 655 int paddingBits = (paramBytes * 8) - paramBits; 656 outStream.write(8, paramBytes); 657 outStream.write(5, bData.userData.msgEncoding); 658 if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) || 659 (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) { 660 outStream.write(8, bData.userData.msgType); 661 } 662 outStream.write(8, bData.userData.numFields); 663 outStream.writeByteArray(dataBits, bData.userData.payload); 664 if (paddingBits > 0) outStream.write(paddingBits, 0); 665 } 666 667 private static void encodeReplyOption(BearerData bData, BitwiseOutputStream outStream) 668 throws BitwiseOutputStream.AccessException 669 { 670 outStream.write(8, 1); 671 outStream.write(1, bData.userAckReq ? 1 : 0); 672 outStream.write(1, bData.deliveryAckReq ? 1 : 0); 673 outStream.write(1, bData.readAckReq ? 1 : 0); 674 outStream.write(1, bData.reportReq ? 1 : 0); 675 outStream.write(4, 0); 676 } 677 678 private static byte[] encodeDtmfSmsAddress(String address) { 679 int digits = address.length(); 680 int dataBits = digits * 4; 681 int dataBytes = (dataBits / 8); 682 dataBytes += (dataBits % 8) > 0 ? 1 : 0; 683 byte[] rawData = new byte[dataBytes]; 684 for (int i = 0; i < digits; i++) { 685 char c = address.charAt(i); 686 int val = 0; 687 if ((c >= '1') && (c <= '9')) val = c - '0'; 688 else if (c == '0') val = 10; 689 else if (c == '*') val = 11; 690 else if (c == '#') val = 12; 691 else return null; 692 rawData[i / 2] |= val << (4 - ((i % 2) * 4)); 693 } 694 return rawData; 695 } 696 697 /* 698 * TODO(cleanup): CdmaSmsAddress encoding should make use of 699 * CdmaSmsAddress.parse provided that DTMF encoding is unified, 700 * and the difference in 4bit vs 8bit is resolved. 701 */ 702 703 private static void encodeCdmaSmsAddress(CdmaSmsAddress addr) throws CodingException { 704 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 705 try { 706 addr.origBytes = addr.address.getBytes("US-ASCII"); 707 } catch (java.io.UnsupportedEncodingException ex) { 708 throw new CodingException("invalid SMS address, cannot convert to ASCII"); 709 } 710 } else { 711 addr.origBytes = encodeDtmfSmsAddress(addr.address); 712 } 713 } 714 715 private static void encodeCallbackNumber(BearerData bData, BitwiseOutputStream outStream) 716 throws BitwiseOutputStream.AccessException, CodingException 717 { 718 CdmaSmsAddress addr = bData.callbackNumber; 719 encodeCdmaSmsAddress(addr); 720 int paramBits = 9; 721 int dataBits = 0; 722 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 723 paramBits += 7; 724 dataBits = addr.numberOfDigits * 8; 725 } else { 726 dataBits = addr.numberOfDigits * 4; 727 } 728 paramBits += dataBits; 729 int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0); 730 int paddingBits = (paramBytes * 8) - paramBits; 731 outStream.write(8, paramBytes); 732 outStream.write(1, addr.digitMode); 733 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 734 outStream.write(3, addr.ton); 735 outStream.write(4, addr.numberPlan); 736 } 737 outStream.write(8, addr.numberOfDigits); 738 outStream.writeByteArray(dataBits, addr.origBytes); 739 if (paddingBits > 0) outStream.write(paddingBits, 0); 740 } 741 742 private static void encodeMsgStatus(BearerData bData, BitwiseOutputStream outStream) 743 throws BitwiseOutputStream.AccessException 744 { 745 outStream.write(8, 1); 746 outStream.write(2, bData.errorClass); 747 outStream.write(6, bData.messageStatus); 748 } 749 750 private static void encodeMsgCount(BearerData bData, BitwiseOutputStream outStream) 751 throws BitwiseOutputStream.AccessException 752 { 753 outStream.write(8, 1); 754 outStream.write(8, bData.numberOfMessages); 755 } 756 757 private static void encodeValidityPeriodRel(BearerData bData, BitwiseOutputStream outStream) 758 throws BitwiseOutputStream.AccessException 759 { 760 outStream.write(8, 1); 761 outStream.write(8, bData.validityPeriodRelative); 762 } 763 764 private static void encodePrivacyIndicator(BearerData bData, BitwiseOutputStream outStream) 765 throws BitwiseOutputStream.AccessException 766 { 767 outStream.write(8, 1); 768 outStream.write(2, bData.privacy); 769 outStream.skip(6); 770 } 771 772 private static void encodeLanguageIndicator(BearerData bData, BitwiseOutputStream outStream) 773 throws BitwiseOutputStream.AccessException 774 { 775 outStream.write(8, 1); 776 outStream.write(8, bData.language); 777 } 778 779 private static void encodeDisplayMode(BearerData bData, BitwiseOutputStream outStream) 780 throws BitwiseOutputStream.AccessException 781 { 782 outStream.write(8, 1); 783 outStream.write(2, bData.displayMode); 784 outStream.skip(6); 785 } 786 787 private static void encodePriorityIndicator(BearerData bData, BitwiseOutputStream outStream) 788 throws BitwiseOutputStream.AccessException 789 { 790 outStream.write(8, 1); 791 outStream.write(2, bData.priority); 792 outStream.skip(6); 793 } 794 795 private static void encodeMsgDeliveryAlert(BearerData bData, BitwiseOutputStream outStream) 796 throws BitwiseOutputStream.AccessException 797 { 798 outStream.write(8, 1); 799 outStream.write(2, bData.alert); 800 outStream.skip(6); 801 } 802 803 /** 804 * Create serialized representation for BearerData object. 805 * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details) 806 * 807 * @param bData an instance of BearerData. 808 * @return byte array of raw encoded SMS bearer data. 809 */ 810 public static byte[] encode(BearerData bData) { 811 bData.hasUserDataHeader = ((bData.userData != null) && 812 (bData.userData.userDataHeader != null)); 813 try { 814 BitwiseOutputStream outStream = new BitwiseOutputStream(200); 815 outStream.write(8, SUBPARAM_MESSAGE_IDENTIFIER); 816 encodeMessageId(bData, outStream); 817 if (bData.userData != null) { 818 outStream.write(8, SUBPARAM_USER_DATA); 819 encodeUserData(bData, outStream); 820 } 821 if (bData.callbackNumber != null) { 822 outStream.write(8, SUBPARAM_CALLBACK_NUMBER); 823 encodeCallbackNumber(bData, outStream); 824 } 825 if (bData.userAckReq || bData.deliveryAckReq || bData.readAckReq || bData.reportReq) { 826 outStream.write(8, SUBPARAM_REPLY_OPTION); 827 encodeReplyOption(bData, outStream); 828 } 829 if (bData.numberOfMessages != 0) { 830 outStream.write(8, SUBPARAM_NUMBER_OF_MESSAGES); 831 encodeMsgCount(bData, outStream); 832 } 833 if (bData.validityPeriodRelativeSet) { 834 outStream.write(8, SUBPARAM_VALIDITY_PERIOD_RELATIVE); 835 encodeValidityPeriodRel(bData, outStream); 836 } 837 if (bData.privacyIndicatorSet) { 838 outStream.write(8, SUBPARAM_PRIVACY_INDICATOR); 839 encodePrivacyIndicator(bData, outStream); 840 } 841 if (bData.languageIndicatorSet) { 842 outStream.write(8, SUBPARAM_LANGUAGE_INDICATOR); 843 encodeLanguageIndicator(bData, outStream); 844 } 845 if (bData.displayModeSet) { 846 outStream.write(8, SUBPARAM_MESSAGE_DISPLAY_MODE); 847 encodeDisplayMode(bData, outStream); 848 } 849 if (bData.priorityIndicatorSet) { 850 outStream.write(8, SUBPARAM_PRIORITY_INDICATOR); 851 encodePriorityIndicator(bData, outStream); 852 } 853 if (bData.alertIndicatorSet) { 854 outStream.write(8, SUBPARAM_ALERT_ON_MESSAGE_DELIVERY); 855 encodeMsgDeliveryAlert(bData, outStream); 856 } 857 if (bData.messageStatusSet) { 858 outStream.write(8, SUBPARAM_MESSAGE_STATUS); 859 encodeMsgStatus(bData, outStream); 860 } 861 return outStream.toByteArray(); 862 } catch (BitwiseOutputStream.AccessException ex) { 863 Log.e(LOG_TAG, "BearerData encode failed: " + ex); 864 } catch (CodingException ex) { 865 Log.e(LOG_TAG, "BearerData encode failed: " + ex); 866 } 867 return null; 868 } 869 870 private static boolean decodeMessageId(BearerData bData, BitwiseInputStream inStream) 871 throws BitwiseInputStream.AccessException, CodingException 872 { 873 final int EXPECTED_PARAM_SIZE = 3 * 8; 874 boolean decodeSuccess = false; 875 int paramBits = inStream.read(8) * 8; 876 if (paramBits >= EXPECTED_PARAM_SIZE) { 877 paramBits -= EXPECTED_PARAM_SIZE; 878 decodeSuccess = true; 879 bData.messageType = inStream.read(4); 880 bData.messageId = inStream.read(8) << 8; 881 bData.messageId |= inStream.read(8); 882 bData.hasUserDataHeader = (inStream.read(1) == 1); 883 inStream.skip(3); 884 } 885 if ((! decodeSuccess) || (paramBits > 0)) { 886 Log.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " + 887 (decodeSuccess ? "succeeded" : "failed") + 888 " (extra bits = " + paramBits + ")"); 889 } 890 inStream.skip(paramBits); 891 return decodeSuccess; 892 } 893 894 private static boolean decodeUserData(BearerData bData, BitwiseInputStream inStream) 895 throws BitwiseInputStream.AccessException 896 { 897 int paramBits = inStream.read(8) * 8; 898 bData.userData = new UserData(); 899 bData.userData.msgEncoding = inStream.read(5); 900 bData.userData.msgEncodingSet = true; 901 bData.userData.msgType = 0; 902 int consumedBits = 5; 903 if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) || 904 (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) { 905 bData.userData.msgType = inStream.read(8); 906 consumedBits += 8; 907 } 908 bData.userData.numFields = inStream.read(8); 909 consumedBits += 8; 910 int dataBits = paramBits - consumedBits; 911 bData.userData.payload = inStream.readByteArray(dataBits); 912 return true; 913 } 914 915 private static String decodeUtf8(byte[] data, int offset, int numFields) 916 throws CodingException 917 { 918 try { 919 return new String(data, offset, numFields, "UTF-8"); 920 } catch (java.io.UnsupportedEncodingException ex) { 921 throw new CodingException("UTF-8 decode failed: " + ex); 922 } 923 } 924 925 private static String decodeUtf16(byte[] data, int offset, int numFields) 926 throws CodingException 927 { 928 // Start reading from the next 16-bit aligned boundary after offset. 929 int padding = offset % 2; 930 numFields -= (offset + padding) / 2; 931 try { 932 return new String(data, offset, numFields * 2, "utf-16be"); 933 } catch (java.io.UnsupportedEncodingException ex) { 934 throw new CodingException("UTF-16 decode failed: " + ex); 935 } 936 } 937 938 private static String decode7bitAscii(byte[] data, int offset, int numFields) 939 throws CodingException 940 { 941 try { 942 offset *= 8; 943 StringBuffer strBuf = new StringBuffer(numFields); 944 BitwiseInputStream inStream = new BitwiseInputStream(data); 945 int wantedBits = (offset * 8) + (numFields * 7); 946 if (inStream.available() < wantedBits) { 947 throw new CodingException("insufficient data (wanted " + wantedBits + 948 " bits, but only have " + inStream.available() + ")"); 949 } 950 inStream.skip(offset); 951 for (int i = 0; i < numFields; i++) { 952 int charCode = inStream.read(7); 953 if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) && 954 (charCode <= UserData.ASCII_MAP_MAX_INDEX)) { 955 strBuf.append(UserData.ASCII_MAP[charCode - UserData.ASCII_MAP_BASE_INDEX]); 956 } else if (charCode == UserData.ASCII_NL_INDEX) { 957 strBuf.append('\n'); 958 } else if (charCode == UserData.ASCII_CR_INDEX) { 959 strBuf.append('\r'); 960 } else { 961 /* For other charCodes, they are unprintable, and so simply use SPACE. */ 962 strBuf.append(' '); 963 } 964 } 965 return strBuf.toString(); 966 } catch (BitwiseInputStream.AccessException ex) { 967 throw new CodingException("7bit ASCII decode failed: " + ex); 968 } 969 } 970 971 private static String decode7bitGsm(byte[] data, int offset, int numFields) 972 throws CodingException 973 { 974 // Start reading from the next 7-bit aligned boundary after offset. 975 int offsetBits = offset * 8; 976 int offsetSeptets = (offsetBits + 6) / 7; 977 numFields -= offsetSeptets; 978 int paddingBits = (offsetSeptets * 7) - offsetBits; 979 String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits, 980 0, 0); 981 if (result == null) { 982 throw new CodingException("7bit GSM decoding failed"); 983 } 984 return result; 985 } 986 987 private static String decodeLatin(byte[] data, int offset, int numFields) 988 throws CodingException 989 { 990 try { 991 return new String(data, offset, numFields - offset, "ISO-8859-1"); 992 } catch (java.io.UnsupportedEncodingException ex) { 993 throw new CodingException("ISO-8859-1 decode failed: " + ex); 994 } 995 } 996 997 private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader) 998 throws CodingException 999 { 1000 int offset = 0; 1001 if (hasUserDataHeader) { 1002 int udhLen = userData.payload[0] & 0x00FF; 1003 offset += udhLen + 1; 1004 byte[] headerData = new byte[udhLen]; 1005 System.arraycopy(userData.payload, 1, headerData, 0, udhLen); 1006 userData.userDataHeader = SmsHeader.fromByteArray(headerData); 1007 } 1008 switch (userData.msgEncoding) { 1009 case UserData.ENCODING_OCTET: 1010 /* 1011 * Octet decoding depends on the carrier service. 1012 */ 1013 boolean decodingtypeUTF8 = Resources.getSystem() 1014 .getBoolean(com.android.internal.R.bool.config_sms_utf8_support); 1015 1016 // Strip off any padding bytes, meaning any differences between the length of the 1017 // array and the target length specified by numFields. This is to avoid any 1018 // confusion by code elsewhere that only considers the payload array length. 1019 byte[] payload = new byte[userData.numFields]; 1020 int copyLen = userData.numFields < userData.payload.length 1021 ? userData.numFields : userData.payload.length; 1022 1023 System.arraycopy(userData.payload, 0, payload, 0, copyLen); 1024 userData.payload = payload; 1025 1026 if (!decodingtypeUTF8) { 1027 // There are many devices in the market that send 8bit text sms (latin encoded) as 1028 // octet encoded. 1029 userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields); 1030 } else { 1031 userData.payloadStr = decodeUtf8(userData.payload, offset, userData.numFields); 1032 } 1033 break; 1034 case UserData.ENCODING_IA5: 1035 case UserData.ENCODING_7BIT_ASCII: 1036 userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields); 1037 break; 1038 case UserData.ENCODING_UNICODE_16: 1039 userData.payloadStr = decodeUtf16(userData.payload, offset, userData.numFields); 1040 break; 1041 case UserData.ENCODING_GSM_7BIT_ALPHABET: 1042 userData.payloadStr = decode7bitGsm(userData.payload, offset, userData.numFields); 1043 break; 1044 case UserData.ENCODING_LATIN: 1045 userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields); 1046 break; 1047 default: 1048 throw new CodingException("unsupported user data encoding (" 1049 + userData.msgEncoding + ")"); 1050 } 1051 } 1052 1053 /** 1054 * IS-91 Voice Mail message decoding 1055 * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) 1056 * (For character encodings, see TIA/EIA/IS-91, Annex B) 1057 * 1058 * Protocol Summary: The user data payload may contain 3-14 1059 * characters. The first two characters are parsed as a number 1060 * and indicate the number of voicemails. The third character is 1061 * either a SPACE or '!' to indicate normal or urgent priority, 1062 * respectively. Any following characters are treated as normal 1063 * text user data payload. 1064 * 1065 * Note that the characters encoding is 6-bit packed. 1066 */ 1067 private static void decodeIs91VoicemailStatus(BearerData bData) 1068 throws BitwiseInputStream.AccessException, CodingException 1069 { 1070 BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); 1071 int dataLen = inStream.available() / 6; // 6-bit packed character encoding. 1072 int numFields = bData.userData.numFields; 1073 if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) { 1074 throw new CodingException("IS-91 voicemail status decoding failed"); 1075 } 1076 try { 1077 StringBuffer strbuf = new StringBuffer(dataLen); 1078 while (inStream.available() >= 6) { 1079 strbuf.append(UserData.ASCII_MAP[inStream.read(6)]); 1080 } 1081 String data = strbuf.toString(); 1082 bData.numberOfMessages = Integer.parseInt(data.substring(0, 2)); 1083 char prioCode = data.charAt(2); 1084 if (prioCode == ' ') { 1085 bData.priority = PRIORITY_NORMAL; 1086 } else if (prioCode == '!') { 1087 bData.priority = PRIORITY_URGENT; 1088 } else { 1089 throw new CodingException("IS-91 voicemail status decoding failed: " + 1090 "illegal priority setting (" + prioCode + ")"); 1091 } 1092 bData.priorityIndicatorSet = true; 1093 bData.userData.payloadStr = data.substring(3, numFields - 3); 1094 } catch (java.lang.NumberFormatException ex) { 1095 throw new CodingException("IS-91 voicemail status decoding failed: " + ex); 1096 } catch (java.lang.IndexOutOfBoundsException ex) { 1097 throw new CodingException("IS-91 voicemail status decoding failed: " + ex); 1098 } 1099 } 1100 1101 /** 1102 * IS-91 Short Message decoding 1103 * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) 1104 * (For character encodings, see TIA/EIA/IS-91, Annex B) 1105 * 1106 * Protocol Summary: The user data payload may contain 1-14 1107 * characters, which are treated as normal text user data payload. 1108 * Note that the characters encoding is 6-bit packed. 1109 */ 1110 private static void decodeIs91ShortMessage(BearerData bData) 1111 throws BitwiseInputStream.AccessException, CodingException 1112 { 1113 BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); 1114 int dataLen = inStream.available() / 6; // 6-bit packed character encoding. 1115 int numFields = bData.userData.numFields; 1116 if ((dataLen > 14) || (dataLen < numFields)) { 1117 throw new CodingException("IS-91 voicemail status decoding failed"); 1118 } 1119 StringBuffer strbuf = new StringBuffer(dataLen); 1120 for (int i = 0; i < numFields; i++) { 1121 strbuf.append(UserData.ASCII_MAP[inStream.read(6)]); 1122 } 1123 bData.userData.payloadStr = strbuf.toString(); 1124 } 1125 1126 /** 1127 * IS-91 CLI message (callback number) decoding 1128 * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) 1129 * 1130 * Protocol Summary: The data payload may contain 1-32 digits, 1131 * encoded using standard 4-bit DTMF, which are treated as a 1132 * callback number. 1133 */ 1134 private static void decodeIs91Cli(BearerData bData) throws CodingException { 1135 BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); 1136 int dataLen = inStream.available() / 4; // 4-bit packed DTMF digit encoding. 1137 int numFields = bData.userData.numFields; 1138 if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) { 1139 throw new CodingException("IS-91 voicemail status decoding failed"); 1140 } 1141 CdmaSmsAddress addr = new CdmaSmsAddress(); 1142 addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF; 1143 addr.origBytes = bData.userData.payload; 1144 addr.numberOfDigits = (byte)numFields; 1145 decodeSmsAddress(addr); 1146 bData.callbackNumber = addr; 1147 } 1148 1149 private static void decodeIs91(BearerData bData) 1150 throws BitwiseInputStream.AccessException, CodingException 1151 { 1152 switch (bData.userData.msgType) { 1153 case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS: 1154 decodeIs91VoicemailStatus(bData); 1155 break; 1156 case UserData.IS91_MSG_TYPE_CLI: 1157 decodeIs91Cli(bData); 1158 break; 1159 case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL: 1160 case UserData.IS91_MSG_TYPE_SHORT_MESSAGE: 1161 decodeIs91ShortMessage(bData); 1162 break; 1163 default: 1164 throw new CodingException("unsupported IS-91 message type (" + 1165 bData.userData.msgType + ")"); 1166 } 1167 } 1168 1169 private static boolean decodeReplyOption(BearerData bData, BitwiseInputStream inStream) 1170 throws BitwiseInputStream.AccessException, CodingException 1171 { 1172 final int EXPECTED_PARAM_SIZE = 1 * 8; 1173 boolean decodeSuccess = false; 1174 int paramBits = inStream.read(8) * 8; 1175 if (paramBits >= EXPECTED_PARAM_SIZE) { 1176 paramBits -= EXPECTED_PARAM_SIZE; 1177 decodeSuccess = true; 1178 bData.userAckReq = (inStream.read(1) == 1); 1179 bData.deliveryAckReq = (inStream.read(1) == 1); 1180 bData.readAckReq = (inStream.read(1) == 1); 1181 bData.reportReq = (inStream.read(1) == 1); 1182 inStream.skip(4); 1183 } 1184 if ((! decodeSuccess) || (paramBits > 0)) { 1185 Log.d(LOG_TAG, "REPLY_OPTION decode " + 1186 (decodeSuccess ? "succeeded" : "failed") + 1187 " (extra bits = " + paramBits + ")"); 1188 } 1189 inStream.skip(paramBits); 1190 return decodeSuccess; 1191 } 1192 1193 private static boolean decodeMsgCount(BearerData bData, BitwiseInputStream inStream) 1194 throws BitwiseInputStream.AccessException, CodingException 1195 { 1196 final int EXPECTED_PARAM_SIZE = 1 * 8; 1197 boolean decodeSuccess = false; 1198 int paramBits = inStream.read(8) * 8; 1199 if (paramBits >= EXPECTED_PARAM_SIZE) { 1200 paramBits -= EXPECTED_PARAM_SIZE; 1201 decodeSuccess = true; 1202 bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8)); 1203 } 1204 if ((! decodeSuccess) || (paramBits > 0)) { 1205 Log.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " + 1206 (decodeSuccess ? "succeeded" : "failed") + 1207 " (extra bits = " + paramBits + ")"); 1208 } 1209 inStream.skip(paramBits); 1210 return decodeSuccess; 1211 } 1212 1213 private static boolean decodeDepositIndex(BearerData bData, BitwiseInputStream inStream) 1214 throws BitwiseInputStream.AccessException, CodingException 1215 { 1216 final int EXPECTED_PARAM_SIZE = 2 * 8; 1217 boolean decodeSuccess = false; 1218 int paramBits = inStream.read(8) * 8; 1219 if (paramBits >= EXPECTED_PARAM_SIZE) { 1220 paramBits -= EXPECTED_PARAM_SIZE; 1221 decodeSuccess = true; 1222 bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8); 1223 } 1224 if ((! decodeSuccess) || (paramBits > 0)) { 1225 Log.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " + 1226 (decodeSuccess ? "succeeded" : "failed") + 1227 " (extra bits = " + paramBits + ")"); 1228 } 1229 inStream.skip(paramBits); 1230 return decodeSuccess; 1231 } 1232 1233 private static String decodeDtmfSmsAddress(byte[] rawData, int numFields) 1234 throws CodingException 1235 { 1236 /* DTMF 4-bit digit encoding, defined in at 1237 * 3GPP2 C.S005-D, v2.0, table 2.7.1.3.2.4-4 */ 1238 StringBuffer strBuf = new StringBuffer(numFields); 1239 for (int i = 0; i < numFields; i++) { 1240 int val = 0x0F & (rawData[i / 2] >>> (4 - ((i % 2) * 4))); 1241 if ((val >= 1) && (val <= 9)) strBuf.append(Integer.toString(val, 10)); 1242 else if (val == 10) strBuf.append('0'); 1243 else if (val == 11) strBuf.append('*'); 1244 else if (val == 12) strBuf.append('#'); 1245 else throw new CodingException("invalid SMS address DTMF code (" + val + ")"); 1246 } 1247 return strBuf.toString(); 1248 } 1249 1250 private static void decodeSmsAddress(CdmaSmsAddress addr) throws CodingException { 1251 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 1252 try { 1253 /* As specified in 3GPP2 C.S0015-B, v2, 4.5.15 -- actually 1254 * just 7-bit ASCII encoding, with the MSB being zero. */ 1255 addr.address = new String(addr.origBytes, 0, addr.origBytes.length, "US-ASCII"); 1256 } catch (java.io.UnsupportedEncodingException ex) { 1257 throw new CodingException("invalid SMS address ASCII code"); 1258 } 1259 } else { 1260 addr.address = decodeDtmfSmsAddress(addr.origBytes, addr.numberOfDigits); 1261 } 1262 } 1263 1264 private static boolean decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream) 1265 throws BitwiseInputStream.AccessException, CodingException 1266 { 1267 int paramBits = inStream.read(8) * 8; 1268 CdmaSmsAddress addr = new CdmaSmsAddress(); 1269 addr.digitMode = inStream.read(1); 1270 byte fieldBits = 4; 1271 byte consumedBits = 1; 1272 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 1273 addr.ton = inStream.read(3); 1274 addr.numberPlan = inStream.read(4); 1275 fieldBits = 8; 1276 consumedBits += 7; 1277 } 1278 addr.numberOfDigits = inStream.read(8); 1279 consumedBits += 8; 1280 int remainingBits = paramBits - consumedBits; 1281 int dataBits = addr.numberOfDigits * fieldBits; 1282 int paddingBits = remainingBits - dataBits; 1283 if (remainingBits < dataBits) { 1284 throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" + 1285 "remainingBits + " + remainingBits + ", dataBits + " + 1286 dataBits + ", paddingBits + " + paddingBits + ")"); 1287 } 1288 addr.origBytes = inStream.readByteArray(dataBits); 1289 inStream.skip(paddingBits); 1290 decodeSmsAddress(addr); 1291 bData.callbackNumber = addr; 1292 return true; 1293 } 1294 1295 private static boolean decodeMsgStatus(BearerData bData, BitwiseInputStream inStream) 1296 throws BitwiseInputStream.AccessException, CodingException 1297 { 1298 final int EXPECTED_PARAM_SIZE = 1 * 8; 1299 boolean decodeSuccess = false; 1300 int paramBits = inStream.read(8) * 8; 1301 if (paramBits >= EXPECTED_PARAM_SIZE) { 1302 paramBits -= EXPECTED_PARAM_SIZE; 1303 decodeSuccess = true; 1304 bData.errorClass = inStream.read(2); 1305 bData.messageStatus = inStream.read(6); 1306 } 1307 if ((! decodeSuccess) || (paramBits > 0)) { 1308 Log.d(LOG_TAG, "MESSAGE_STATUS decode " + 1309 (decodeSuccess ? "succeeded" : "failed") + 1310 " (extra bits = " + paramBits + ")"); 1311 } 1312 inStream.skip(paramBits); 1313 bData.messageStatusSet = decodeSuccess; 1314 return decodeSuccess; 1315 } 1316 1317 private static boolean decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream) 1318 throws BitwiseInputStream.AccessException, CodingException 1319 { 1320 final int EXPECTED_PARAM_SIZE = 6 * 8; 1321 boolean decodeSuccess = false; 1322 int paramBits = inStream.read(8) * 8; 1323 if (paramBits >= EXPECTED_PARAM_SIZE) { 1324 paramBits -= EXPECTED_PARAM_SIZE; 1325 decodeSuccess = true; 1326 bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8)); 1327 } 1328 if ((! decodeSuccess) || (paramBits > 0)) { 1329 Log.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " + 1330 (decodeSuccess ? "succeeded" : "failed") + 1331 " (extra bits = " + paramBits + ")"); 1332 } 1333 inStream.skip(paramBits); 1334 return decodeSuccess; 1335 } 1336 1337 private static boolean decodeValidityAbs(BearerData bData, BitwiseInputStream inStream) 1338 throws BitwiseInputStream.AccessException, CodingException 1339 { 1340 final int EXPECTED_PARAM_SIZE = 6 * 8; 1341 boolean decodeSuccess = false; 1342 int paramBits = inStream.read(8) * 8; 1343 if (paramBits >= EXPECTED_PARAM_SIZE) { 1344 paramBits -= EXPECTED_PARAM_SIZE; 1345 decodeSuccess = true; 1346 bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8)); 1347 } 1348 if ((! decodeSuccess) || (paramBits > 0)) { 1349 Log.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " + 1350 (decodeSuccess ? "succeeded" : "failed") + 1351 " (extra bits = " + paramBits + ")"); 1352 } 1353 inStream.skip(paramBits); 1354 return decodeSuccess; 1355 } 1356 1357 private static boolean decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream) 1358 throws BitwiseInputStream.AccessException, CodingException 1359 { 1360 final int EXPECTED_PARAM_SIZE = 6 * 8; 1361 boolean decodeSuccess = false; 1362 int paramBits = inStream.read(8) * 8; 1363 if (paramBits >= EXPECTED_PARAM_SIZE) { 1364 paramBits -= EXPECTED_PARAM_SIZE; 1365 decodeSuccess = true; 1366 bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray( 1367 inStream.readByteArray(6 * 8)); 1368 } 1369 if ((! decodeSuccess) || (paramBits > 0)) { 1370 Log.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " + 1371 (decodeSuccess ? "succeeded" : "failed") + 1372 " (extra bits = " + paramBits + ")"); 1373 } 1374 inStream.skip(paramBits); 1375 return decodeSuccess; 1376 } 1377 1378 private static boolean decodeValidityRel(BearerData bData, BitwiseInputStream inStream) 1379 throws BitwiseInputStream.AccessException, CodingException 1380 { 1381 final int EXPECTED_PARAM_SIZE = 1 * 8; 1382 boolean decodeSuccess = false; 1383 int paramBits = inStream.read(8) * 8; 1384 if (paramBits >= EXPECTED_PARAM_SIZE) { 1385 paramBits -= EXPECTED_PARAM_SIZE; 1386 decodeSuccess = true; 1387 bData.deferredDeliveryTimeRelative = inStream.read(8); 1388 } 1389 if ((! decodeSuccess) || (paramBits > 0)) { 1390 Log.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " + 1391 (decodeSuccess ? "succeeded" : "failed") + 1392 " (extra bits = " + paramBits + ")"); 1393 } 1394 inStream.skip(paramBits); 1395 bData.deferredDeliveryTimeRelativeSet = decodeSuccess; 1396 return decodeSuccess; 1397 } 1398 1399 private static boolean decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream) 1400 throws BitwiseInputStream.AccessException, CodingException 1401 { 1402 final int EXPECTED_PARAM_SIZE = 1 * 8; 1403 boolean decodeSuccess = false; 1404 int paramBits = inStream.read(8) * 8; 1405 if (paramBits >= EXPECTED_PARAM_SIZE) { 1406 paramBits -= EXPECTED_PARAM_SIZE; 1407 decodeSuccess = true; 1408 bData.validityPeriodRelative = inStream.read(8); 1409 } 1410 if ((! decodeSuccess) || (paramBits > 0)) { 1411 Log.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " + 1412 (decodeSuccess ? "succeeded" : "failed") + 1413 " (extra bits = " + paramBits + ")"); 1414 } 1415 inStream.skip(paramBits); 1416 bData.validityPeriodRelativeSet = decodeSuccess; 1417 return decodeSuccess; 1418 } 1419 1420 private static boolean decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream) 1421 throws BitwiseInputStream.AccessException, CodingException 1422 { 1423 final int EXPECTED_PARAM_SIZE = 1 * 8; 1424 boolean decodeSuccess = false; 1425 int paramBits = inStream.read(8) * 8; 1426 if (paramBits >= EXPECTED_PARAM_SIZE) { 1427 paramBits -= EXPECTED_PARAM_SIZE; 1428 decodeSuccess = true; 1429 bData.privacy = inStream.read(2); 1430 inStream.skip(6); 1431 } 1432 if ((! decodeSuccess) || (paramBits > 0)) { 1433 Log.d(LOG_TAG, "PRIVACY_INDICATOR decode " + 1434 (decodeSuccess ? "succeeded" : "failed") + 1435 " (extra bits = " + paramBits + ")"); 1436 } 1437 inStream.skip(paramBits); 1438 bData.privacyIndicatorSet = decodeSuccess; 1439 return decodeSuccess; 1440 } 1441 1442 private static boolean decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream) 1443 throws BitwiseInputStream.AccessException, CodingException 1444 { 1445 final int EXPECTED_PARAM_SIZE = 1 * 8; 1446 boolean decodeSuccess = false; 1447 int paramBits = inStream.read(8) * 8; 1448 if (paramBits >= EXPECTED_PARAM_SIZE) { 1449 paramBits -= EXPECTED_PARAM_SIZE; 1450 decodeSuccess = true; 1451 bData.language = inStream.read(8); 1452 } 1453 if ((! decodeSuccess) || (paramBits > 0)) { 1454 Log.d(LOG_TAG, "LANGUAGE_INDICATOR decode " + 1455 (decodeSuccess ? "succeeded" : "failed") + 1456 " (extra bits = " + paramBits + ")"); 1457 } 1458 inStream.skip(paramBits); 1459 bData.languageIndicatorSet = decodeSuccess; 1460 return decodeSuccess; 1461 } 1462 1463 private static boolean decodeDisplayMode(BearerData bData, BitwiseInputStream inStream) 1464 throws BitwiseInputStream.AccessException, CodingException 1465 { 1466 final int EXPECTED_PARAM_SIZE = 1 * 8; 1467 boolean decodeSuccess = false; 1468 int paramBits = inStream.read(8) * 8; 1469 if (paramBits >= EXPECTED_PARAM_SIZE) { 1470 paramBits -= EXPECTED_PARAM_SIZE; 1471 decodeSuccess = true; 1472 bData.displayMode = inStream.read(2); 1473 inStream.skip(6); 1474 } 1475 if ((! decodeSuccess) || (paramBits > 0)) { 1476 Log.d(LOG_TAG, "DISPLAY_MODE decode " + 1477 (decodeSuccess ? "succeeded" : "failed") + 1478 " (extra bits = " + paramBits + ")"); 1479 } 1480 inStream.skip(paramBits); 1481 bData.displayModeSet = decodeSuccess; 1482 return decodeSuccess; 1483 } 1484 1485 private static boolean decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream) 1486 throws BitwiseInputStream.AccessException, CodingException 1487 { 1488 final int EXPECTED_PARAM_SIZE = 1 * 8; 1489 boolean decodeSuccess = false; 1490 int paramBits = inStream.read(8) * 8; 1491 if (paramBits >= EXPECTED_PARAM_SIZE) { 1492 paramBits -= EXPECTED_PARAM_SIZE; 1493 decodeSuccess = true; 1494 bData.priority = inStream.read(2); 1495 inStream.skip(6); 1496 } 1497 if ((! decodeSuccess) || (paramBits > 0)) { 1498 Log.d(LOG_TAG, "PRIORITY_INDICATOR decode " + 1499 (decodeSuccess ? "succeeded" : "failed") + 1500 " (extra bits = " + paramBits + ")"); 1501 } 1502 inStream.skip(paramBits); 1503 bData.priorityIndicatorSet = decodeSuccess; 1504 return decodeSuccess; 1505 } 1506 1507 private static boolean decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream) 1508 throws BitwiseInputStream.AccessException, CodingException 1509 { 1510 final int EXPECTED_PARAM_SIZE = 1 * 8; 1511 boolean decodeSuccess = false; 1512 int paramBits = inStream.read(8) * 8; 1513 if (paramBits >= EXPECTED_PARAM_SIZE) { 1514 paramBits -= EXPECTED_PARAM_SIZE; 1515 decodeSuccess = true; 1516 bData.alert = inStream.read(2); 1517 inStream.skip(6); 1518 } 1519 if ((! decodeSuccess) || (paramBits > 0)) { 1520 Log.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " + 1521 (decodeSuccess ? "succeeded" : "failed") + 1522 " (extra bits = " + paramBits + ")"); 1523 } 1524 inStream.skip(paramBits); 1525 bData.alertIndicatorSet = decodeSuccess; 1526 return decodeSuccess; 1527 } 1528 1529 private static boolean decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream) 1530 throws BitwiseInputStream.AccessException, CodingException 1531 { 1532 final int EXPECTED_PARAM_SIZE = 1 * 8; 1533 boolean decodeSuccess = false; 1534 int paramBits = inStream.read(8) * 8; 1535 if (paramBits >= EXPECTED_PARAM_SIZE) { 1536 paramBits -= EXPECTED_PARAM_SIZE; 1537 decodeSuccess = true; 1538 bData.userResponseCode = inStream.read(8); 1539 } 1540 if ((! decodeSuccess) || (paramBits > 0)) { 1541 Log.d(LOG_TAG, "USER_REPONSE_CODE decode " + 1542 (decodeSuccess ? "succeeded" : "failed") + 1543 " (extra bits = " + paramBits + ")"); 1544 } 1545 inStream.skip(paramBits); 1546 bData.userResponseCodeSet = decodeSuccess; 1547 return decodeSuccess; 1548 } 1549 1550 /** 1551 * Create BearerData object from serialized representation. 1552 * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details) 1553 * 1554 * @param smsData byte array of raw encoded SMS bearer data. 1555 * 1556 * @return an instance of BearerData. 1557 */ 1558 public static BearerData decode(byte[] smsData) { 1559 try { 1560 BitwiseInputStream inStream = new BitwiseInputStream(smsData); 1561 BearerData bData = new BearerData(); 1562 int foundSubparamMask = 0; 1563 while (inStream.available() > 0) { 1564 boolean decodeSuccess = false; 1565 int subparamId = inStream.read(8); 1566 int subparamIdBit = 1 << subparamId; 1567 if ((foundSubparamMask & subparamIdBit) != 0) { 1568 throw new CodingException("illegal duplicate subparameter (" + 1569 subparamId + ")"); 1570 } 1571 switch (subparamId) { 1572 case SUBPARAM_MESSAGE_IDENTIFIER: 1573 decodeSuccess = decodeMessageId(bData, inStream); 1574 break; 1575 case SUBPARAM_USER_DATA: 1576 decodeSuccess = decodeUserData(bData, inStream); 1577 break; 1578 case SUBPARAM_USER_REPONSE_CODE: 1579 decodeSuccess = decodeUserResponseCode(bData, inStream); 1580 break; 1581 case SUBPARAM_REPLY_OPTION: 1582 decodeSuccess = decodeReplyOption(bData, inStream); 1583 break; 1584 case SUBPARAM_NUMBER_OF_MESSAGES: 1585 decodeSuccess = decodeMsgCount(bData, inStream); 1586 break; 1587 case SUBPARAM_CALLBACK_NUMBER: 1588 decodeSuccess = decodeCallbackNumber(bData, inStream); 1589 break; 1590 case SUBPARAM_MESSAGE_STATUS: 1591 decodeSuccess = decodeMsgStatus(bData, inStream); 1592 break; 1593 case SUBPARAM_MESSAGE_CENTER_TIME_STAMP: 1594 decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream); 1595 break; 1596 case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE: 1597 decodeSuccess = decodeValidityAbs(bData, inStream); 1598 break; 1599 case SUBPARAM_VALIDITY_PERIOD_RELATIVE: 1600 decodeSuccess = decodeValidityRel(bData, inStream); 1601 break; 1602 case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE: 1603 decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream); 1604 break; 1605 case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE: 1606 decodeSuccess = decodeDeferredDeliveryRel(bData, inStream); 1607 break; 1608 case SUBPARAM_PRIVACY_INDICATOR: 1609 decodeSuccess = decodePrivacyIndicator(bData, inStream); 1610 break; 1611 case SUBPARAM_LANGUAGE_INDICATOR: 1612 decodeSuccess = decodeLanguageIndicator(bData, inStream); 1613 break; 1614 case SUBPARAM_MESSAGE_DISPLAY_MODE: 1615 decodeSuccess = decodeDisplayMode(bData, inStream); 1616 break; 1617 case SUBPARAM_PRIORITY_INDICATOR: 1618 decodeSuccess = decodePriorityIndicator(bData, inStream); 1619 break; 1620 case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY: 1621 decodeSuccess = decodeMsgDeliveryAlert(bData, inStream); 1622 break; 1623 case SUBPARAM_MESSAGE_DEPOSIT_INDEX: 1624 decodeSuccess = decodeDepositIndex(bData, inStream); 1625 break; 1626 default: 1627 throw new CodingException("unsupported bearer data subparameter (" 1628 + subparamId + ")"); 1629 } 1630 if (decodeSuccess) foundSubparamMask |= subparamIdBit; 1631 } 1632 if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) { 1633 throw new CodingException("missing MESSAGE_IDENTIFIER subparam"); 1634 } 1635 if (bData.userData != null) { 1636 if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) { 1637 if ((foundSubparamMask ^ 1638 (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^ 1639 (1 << SUBPARAM_USER_DATA)) 1640 != 0) { 1641 Log.e(LOG_TAG, "IS-91 must occur without extra subparams (" + 1642 foundSubparamMask + ")"); 1643 } 1644 decodeIs91(bData); 1645 } else { 1646 decodeUserDataPayload(bData.userData, bData.hasUserDataHeader); 1647 } 1648 } 1649 return bData; 1650 } catch (BitwiseInputStream.AccessException ex) { 1651 Log.e(LOG_TAG, "BearerData decode failed: " + ex); 1652 } catch (CodingException ex) { 1653 Log.e(LOG_TAG, "BearerData decode failed: " + ex); 1654 } 1655 return null; 1656 } 1657} 1658