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