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 import android.util.SparseIntArray; 25 26 import android.telephony.SmsMessage; 27 28 import android.text.format.Time; 29 30 import com.android.internal.telephony.IccUtils; 31 import com.android.internal.telephony.GsmAlphabet; 32 import com.android.internal.telephony.SmsHeader; 33 import com.android.internal.telephony.cdma.sms.UserData; 34 import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; 35 36 import com.android.internal.util.HexDump; 37 import com.android.internal.util.BitwiseInputStream; 38 import com.android.internal.util.BitwiseOutputStream; 39 40 41 /** 42 * An object to encode and decode CDMA SMS bearer data. 43 */ 44 public 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); 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 decodeUtf16(byte[] data, int offset, int numFields) 916 throws CodingException 917 { 918 // Start reading from the next 16-bit aligned boundry after offset. 919 int padding = offset % 2; 920 numFields -= (offset + padding) / 2; 921 try { 922 return new String(data, offset, numFields * 2, "utf-16be"); 923 } catch (java.io.UnsupportedEncodingException ex) { 924 throw new CodingException("UTF-16 decode failed: " + ex); 925 } 926 } 927 928 private static String decode7bitAscii(byte[] data, int offset, int numFields) 929 throws CodingException 930 { 931 try { 932 offset *= 8; 933 StringBuffer strBuf = new StringBuffer(numFields); 934 BitwiseInputStream inStream = new BitwiseInputStream(data); 935 int wantedBits = (offset * 8) + (numFields * 7); 936 if (inStream.available() < wantedBits) { 937 throw new CodingException("insufficient data (wanted " + wantedBits + 938 " bits, but only have " + inStream.available() + ")"); 939 } 940 inStream.skip(offset); 941 for (int i = 0; i < numFields; i++) { 942 int charCode = inStream.read(7); 943 if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) && 944 (charCode <= UserData.ASCII_MAP_MAX_INDEX)) { 945 strBuf.append(UserData.ASCII_MAP[charCode - UserData.ASCII_MAP_BASE_INDEX]); 946 } else if (charCode == UserData.ASCII_NL_INDEX) { 947 strBuf.append('\n'); 948 } else if (charCode == UserData.ASCII_CR_INDEX) { 949 strBuf.append('\r'); 950 } else { 951 /* For other charCodes, they are unprintable, and so simply use SPACE. */ 952 strBuf.append(' '); 953 } 954 } 955 return strBuf.toString(); 956 } catch (BitwiseInputStream.AccessException ex) { 957 throw new CodingException("7bit ASCII decode failed: " + ex); 958 } 959 } 960 961 private static String decode7bitGsm(byte[] data, int offset, int numFields) 962 throws CodingException 963 { 964 // Start reading from the next 7-bit aligned boundry after offset. 965 int offsetBits = offset * 8; 966 int offsetSeptets = (offsetBits + 6) / 7; 967 numFields -= offsetSeptets; 968 int paddingBits = (offsetSeptets * 7) - offsetBits; 969 String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits); 970 if (result == null) { 971 throw new CodingException("7bit GSM decoding failed"); 972 } 973 return result; 974 } 975 976 private static String decodeLatin(byte[] data, int offset, int numFields) 977 throws CodingException 978 { 979 try { 980 return new String(data, offset, numFields - offset, "ISO-8859-1"); 981 } catch (java.io.UnsupportedEncodingException ex) { 982 throw new CodingException("ISO-8859-1 decode failed: " + ex); 983 } 984 } 985 986 private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader) 987 throws CodingException 988 { 989 int offset = 0; 990 if (hasUserDataHeader) { 991 int udhLen = userData.payload[0] & 0x00FF; 992 offset += udhLen + 1; 993 byte[] headerData = new byte[udhLen]; 994 System.arraycopy(userData.payload, 1, headerData, 0, udhLen); 995 userData.userDataHeader = SmsHeader.fromByteArray(headerData); 996 } 997 switch (userData.msgEncoding) { 998 case UserData.ENCODING_OCTET: 999 // Strip off any padding bytes, meaning any differences between the length of the 1000 // array and the target length specified by numFields. This is to avoid any confusion 1001 // by code elsewhere that only considers the payload array length. 1002 byte[] payload = new byte[userData.numFields]; 1003 int copyLen = userData.numFields < userData.payload.length 1004 ? userData.numFields : userData.payload.length; 1005 1006 System.arraycopy(userData.payload, 0, payload, 0, copyLen); 1007 userData.payload = payload; 1008 1009 // There are many devices in the market that send 8bit text sms (latin encoded) as 1010 // octet encoded. 1011 userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields); 1012 break; 1013 case UserData.ENCODING_IA5: 1014 case UserData.ENCODING_7BIT_ASCII: 1015 userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields); 1016 break; 1017 case UserData.ENCODING_UNICODE_16: 1018 userData.payloadStr = decodeUtf16(userData.payload, offset, userData.numFields); 1019 break; 1020 case UserData.ENCODING_GSM_7BIT_ALPHABET: 1021 userData.payloadStr = decode7bitGsm(userData.payload, offset, userData.numFields); 1022 break; 1023 case UserData.ENCODING_LATIN: 1024 userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields); 1025 break; 1026 default: 1027 throw new CodingException("unsupported user data encoding (" 1028 + userData.msgEncoding + ")"); 1029 } 1030 } 1031 1032 /** 1033 * IS-91 Voice Mail message decoding 1034 * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) 1035 * (For character encodings, see TIA/EIA/IS-91, Annex B) 1036 * 1037 * Protocol Summary: The user data payload may contain 3-14 1038 * characters. The first two characters are parsed as a number 1039 * and indicate the number of voicemails. The third character is 1040 * either a SPACE or '!' to indicate normal or urgent priority, 1041 * respectively. Any following characters are treated as normal 1042 * text user data payload. 1043 * 1044 * Note that the characters encoding is 6-bit packed. 1045 */ 1046 private static void decodeIs91VoicemailStatus(BearerData bData) 1047 throws BitwiseInputStream.AccessException, CodingException 1048 { 1049 BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); 1050 int dataLen = inStream.available() / 6; // 6-bit packed character encoding. 1051 int numFields = bData.userData.numFields; 1052 if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) { 1053 throw new CodingException("IS-91 voicemail status decoding failed"); 1054 } 1055 try { 1056 StringBuffer strbuf = new StringBuffer(dataLen); 1057 while (inStream.available() >= 6) { 1058 strbuf.append(UserData.ASCII_MAP[inStream.read(6)]); 1059 } 1060 String data = strbuf.toString(); 1061 bData.numberOfMessages = Integer.parseInt(data.substring(0, 2)); 1062 char prioCode = data.charAt(2); 1063 if (prioCode == ' ') { 1064 bData.priority = PRIORITY_NORMAL; 1065 } else if (prioCode == '!') { 1066 bData.priority = PRIORITY_URGENT; 1067 } else { 1068 throw new CodingException("IS-91 voicemail status decoding failed: " + 1069 "illegal priority setting (" + prioCode + ")"); 1070 } 1071 bData.priorityIndicatorSet = true; 1072 bData.userData.payloadStr = data.substring(3, numFields - 3); 1073 } catch (java.lang.NumberFormatException ex) { 1074 throw new CodingException("IS-91 voicemail status decoding failed: " + ex); 1075 } catch (java.lang.IndexOutOfBoundsException ex) { 1076 throw new CodingException("IS-91 voicemail status decoding failed: " + ex); 1077 } 1078 } 1079 1080 /** 1081 * IS-91 Short Message decoding 1082 * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) 1083 * (For character encodings, see TIA/EIA/IS-91, Annex B) 1084 * 1085 * Protocol Summary: The user data payload may contain 1-14 1086 * characters, which are treated as normal text user data payload. 1087 * Note that the characters encoding is 6-bit packed. 1088 */ 1089 private static void decodeIs91ShortMessage(BearerData bData) 1090 throws BitwiseInputStream.AccessException, CodingException 1091 { 1092 BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); 1093 int dataLen = inStream.available() / 6; // 6-bit packed character encoding. 1094 int numFields = bData.userData.numFields; 1095 if ((dataLen > 14) || (dataLen < numFields)) { 1096 throw new CodingException("IS-91 voicemail status decoding failed"); 1097 } 1098 StringBuffer strbuf = new StringBuffer(dataLen); 1099 for (int i = 0; i < numFields; i++) { 1100 strbuf.append(UserData.ASCII_MAP[inStream.read(6)]); 1101 } 1102 bData.userData.payloadStr = strbuf.toString(); 1103 } 1104 1105 /** 1106 * IS-91 CLI message (callback number) decoding 1107 * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) 1108 * 1109 * Protocol Summary: The data payload may contain 1-32 digits, 1110 * encoded using standard 4-bit DTMF, which are treated as a 1111 * callback number. 1112 */ 1113 private static void decodeIs91Cli(BearerData bData) throws CodingException { 1114 BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); 1115 int dataLen = inStream.available() / 4; // 4-bit packed DTMF digit encoding. 1116 int numFields = bData.userData.numFields; 1117 if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) { 1118 throw new CodingException("IS-91 voicemail status decoding failed"); 1119 } 1120 CdmaSmsAddress addr = new CdmaSmsAddress(); 1121 addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF; 1122 addr.origBytes = bData.userData.payload; 1123 addr.numberOfDigits = (byte)numFields; 1124 decodeSmsAddress(addr); 1125 bData.callbackNumber = addr; 1126 } 1127 1128 private static void decodeIs91(BearerData bData) 1129 throws BitwiseInputStream.AccessException, CodingException 1130 { 1131 switch (bData.userData.msgType) { 1132 case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS: 1133 decodeIs91VoicemailStatus(bData); 1134 break; 1135 case UserData.IS91_MSG_TYPE_CLI: 1136 decodeIs91Cli(bData); 1137 break; 1138 case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL: 1139 case UserData.IS91_MSG_TYPE_SHORT_MESSAGE: 1140 decodeIs91ShortMessage(bData); 1141 break; 1142 default: 1143 throw new CodingException("unsupported IS-91 message type (" + 1144 bData.userData.msgType + ")"); 1145 } 1146 } 1147 1148 private static boolean decodeReplyOption(BearerData bData, BitwiseInputStream inStream) 1149 throws BitwiseInputStream.AccessException, CodingException 1150 { 1151 final int EXPECTED_PARAM_SIZE = 1 * 8; 1152 boolean decodeSuccess = false; 1153 int paramBits = inStream.read(8) * 8; 1154 if (paramBits >= EXPECTED_PARAM_SIZE) { 1155 paramBits -= EXPECTED_PARAM_SIZE; 1156 decodeSuccess = true; 1157 bData.userAckReq = (inStream.read(1) == 1); 1158 bData.deliveryAckReq = (inStream.read(1) == 1); 1159 bData.readAckReq = (inStream.read(1) == 1); 1160 bData.reportReq = (inStream.read(1) == 1); 1161 inStream.skip(4); 1162 } 1163 if ((! decodeSuccess) || (paramBits > 0)) { 1164 Log.d(LOG_TAG, "REPLY_OPTION decode " + 1165 (decodeSuccess ? "succeeded" : "failed") + 1166 " (extra bits = " + paramBits + ")"); 1167 } 1168 inStream.skip(paramBits); 1169 return decodeSuccess; 1170 } 1171 1172 private static boolean decodeMsgCount(BearerData bData, BitwiseInputStream inStream) 1173 throws BitwiseInputStream.AccessException, CodingException 1174 { 1175 final int EXPECTED_PARAM_SIZE = 1 * 8; 1176 boolean decodeSuccess = false; 1177 int paramBits = inStream.read(8) * 8; 1178 if (paramBits >= EXPECTED_PARAM_SIZE) { 1179 paramBits -= EXPECTED_PARAM_SIZE; 1180 decodeSuccess = true; 1181 bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8)); 1182 } 1183 if ((! decodeSuccess) || (paramBits > 0)) { 1184 Log.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " + 1185 (decodeSuccess ? "succeeded" : "failed") + 1186 " (extra bits = " + paramBits + ")"); 1187 } 1188 inStream.skip(paramBits); 1189 return decodeSuccess; 1190 } 1191 1192 private static boolean decodeDepositIndex(BearerData bData, BitwiseInputStream inStream) 1193 throws BitwiseInputStream.AccessException, CodingException 1194 { 1195 final int EXPECTED_PARAM_SIZE = 2 * 8; 1196 boolean decodeSuccess = false; 1197 int paramBits = inStream.read(8) * 8; 1198 if (paramBits >= EXPECTED_PARAM_SIZE) { 1199 paramBits -= EXPECTED_PARAM_SIZE; 1200 decodeSuccess = true; 1201 bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8); 1202 } 1203 if ((! decodeSuccess) || (paramBits > 0)) { 1204 Log.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " + 1205 (decodeSuccess ? "succeeded" : "failed") + 1206 " (extra bits = " + paramBits + ")"); 1207 } 1208 inStream.skip(paramBits); 1209 return decodeSuccess; 1210 } 1211 1212 private static String decodeDtmfSmsAddress(byte[] rawData, int numFields) 1213 throws CodingException 1214 { 1215 /* DTMF 4-bit digit encoding, defined in at 1216 * 3GPP2 C.S005-D, v2.0, table 2.7.1.3.2.4-4 */ 1217 StringBuffer strBuf = new StringBuffer(numFields); 1218 for (int i = 0; i < numFields; i++) { 1219 int val = 0x0F & (rawData[i / 2] >>> (4 - ((i % 2) * 4))); 1220 if ((val >= 1) && (val <= 9)) strBuf.append(Integer.toString(val, 10)); 1221 else if (val == 10) strBuf.append('0'); 1222 else if (val == 11) strBuf.append('*'); 1223 else if (val == 12) strBuf.append('#'); 1224 else throw new CodingException("invalid SMS address DTMF code (" + val + ")"); 1225 } 1226 return strBuf.toString(); 1227 } 1228 1229 private static void decodeSmsAddress(CdmaSmsAddress addr) throws CodingException { 1230 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 1231 try { 1232 /* As specified in 3GPP2 C.S0015-B, v2, 4.5.15 -- actually 1233 * just 7-bit ASCII encoding, with the MSB being zero. */ 1234 addr.address = new String(addr.origBytes, 0, addr.origBytes.length, "US-ASCII"); 1235 } catch (java.io.UnsupportedEncodingException ex) { 1236 throw new CodingException("invalid SMS address ASCII code"); 1237 } 1238 } else { 1239 addr.address = decodeDtmfSmsAddress(addr.origBytes, addr.numberOfDigits); 1240 } 1241 } 1242 1243 private static boolean decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream) 1244 throws BitwiseInputStream.AccessException, CodingException 1245 { 1246 int paramBits = inStream.read(8) * 8; 1247 CdmaSmsAddress addr = new CdmaSmsAddress(); 1248 addr.digitMode = inStream.read(1); 1249 byte fieldBits = 4; 1250 byte consumedBits = 1; 1251 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 1252 addr.ton = inStream.read(3); 1253 addr.numberPlan = inStream.read(4); 1254 fieldBits = 8; 1255 consumedBits += 7; 1256 } 1257 addr.numberOfDigits = inStream.read(8); 1258 consumedBits += 8; 1259 int remainingBits = paramBits - consumedBits; 1260 int dataBits = addr.numberOfDigits * fieldBits; 1261 int paddingBits = remainingBits - dataBits; 1262 if (remainingBits < dataBits) { 1263 throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" + 1264 "remainingBits + " + remainingBits + ", dataBits + " + 1265 dataBits + ", paddingBits + " + paddingBits + ")"); 1266 } 1267 addr.origBytes = inStream.readByteArray(dataBits); 1268 inStream.skip(paddingBits); 1269 decodeSmsAddress(addr); 1270 bData.callbackNumber = addr; 1271 return true; 1272 } 1273 1274 private static boolean decodeMsgStatus(BearerData bData, BitwiseInputStream inStream) 1275 throws BitwiseInputStream.AccessException, CodingException 1276 { 1277 final int EXPECTED_PARAM_SIZE = 1 * 8; 1278 boolean decodeSuccess = false; 1279 int paramBits = inStream.read(8) * 8; 1280 if (paramBits >= EXPECTED_PARAM_SIZE) { 1281 paramBits -= EXPECTED_PARAM_SIZE; 1282 decodeSuccess = true; 1283 bData.errorClass = inStream.read(2); 1284 bData.messageStatus = inStream.read(6); 1285 } 1286 if ((! decodeSuccess) || (paramBits > 0)) { 1287 Log.d(LOG_TAG, "MESSAGE_STATUS decode " + 1288 (decodeSuccess ? "succeeded" : "failed") + 1289 " (extra bits = " + paramBits + ")"); 1290 } 1291 inStream.skip(paramBits); 1292 bData.messageStatusSet = decodeSuccess; 1293 return decodeSuccess; 1294 } 1295 1296 private static boolean decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream) 1297 throws BitwiseInputStream.AccessException, CodingException 1298 { 1299 final int EXPECTED_PARAM_SIZE = 6 * 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.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8)); 1306 } 1307 if ((! decodeSuccess) || (paramBits > 0)) { 1308 Log.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " + 1309 (decodeSuccess ? "succeeded" : "failed") + 1310 " (extra bits = " + paramBits + ")"); 1311 } 1312 inStream.skip(paramBits); 1313 return decodeSuccess; 1314 } 1315 1316 private static boolean decodeValidityAbs(BearerData bData, BitwiseInputStream inStream) 1317 throws BitwiseInputStream.AccessException, CodingException 1318 { 1319 final int EXPECTED_PARAM_SIZE = 6 * 8; 1320 boolean decodeSuccess = false; 1321 int paramBits = inStream.read(8) * 8; 1322 if (paramBits >= EXPECTED_PARAM_SIZE) { 1323 paramBits -= EXPECTED_PARAM_SIZE; 1324 decodeSuccess = true; 1325 bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8)); 1326 } 1327 if ((! decodeSuccess) || (paramBits > 0)) { 1328 Log.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " + 1329 (decodeSuccess ? "succeeded" : "failed") + 1330 " (extra bits = " + paramBits + ")"); 1331 } 1332 inStream.skip(paramBits); 1333 return decodeSuccess; 1334 } 1335 1336 private static boolean decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream) 1337 throws BitwiseInputStream.AccessException, CodingException 1338 { 1339 final int EXPECTED_PARAM_SIZE = 6 * 8; 1340 boolean decodeSuccess = false; 1341 int paramBits = inStream.read(8) * 8; 1342 if (paramBits >= EXPECTED_PARAM_SIZE) { 1343 paramBits -= EXPECTED_PARAM_SIZE; 1344 decodeSuccess = true; 1345 bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray( 1346 inStream.readByteArray(6 * 8)); 1347 } 1348 if ((! decodeSuccess) || (paramBits > 0)) { 1349 Log.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " + 1350 (decodeSuccess ? "succeeded" : "failed") + 1351 " (extra bits = " + paramBits + ")"); 1352 } 1353 inStream.skip(paramBits); 1354 return decodeSuccess; 1355 } 1356 1357 private static boolean decodeValidityRel(BearerData bData, BitwiseInputStream inStream) 1358 throws BitwiseInputStream.AccessException, CodingException 1359 { 1360 final int EXPECTED_PARAM_SIZE = 1 * 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.deferredDeliveryTimeRelative = inStream.read(8); 1367 } 1368 if ((! decodeSuccess) || (paramBits > 0)) { 1369 Log.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " + 1370 (decodeSuccess ? "succeeded" : "failed") + 1371 " (extra bits = " + paramBits + ")"); 1372 } 1373 inStream.skip(paramBits); 1374 bData.deferredDeliveryTimeRelativeSet = decodeSuccess; 1375 return decodeSuccess; 1376 } 1377 1378 private static boolean decodeDeferredDeliveryRel(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.validityPeriodRelative = inStream.read(8); 1388 } 1389 if ((! decodeSuccess) || (paramBits > 0)) { 1390 Log.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " + 1391 (decodeSuccess ? "succeeded" : "failed") + 1392 " (extra bits = " + paramBits + ")"); 1393 } 1394 inStream.skip(paramBits); 1395 bData.validityPeriodRelativeSet = decodeSuccess; 1396 return decodeSuccess; 1397 } 1398 1399 private static boolean decodePrivacyIndicator(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.privacy = inStream.read(2); 1409 inStream.skip(6); 1410 } 1411 if ((! decodeSuccess) || (paramBits > 0)) { 1412 Log.d(LOG_TAG, "PRIVACY_INDICATOR decode " + 1413 (decodeSuccess ? "succeeded" : "failed") + 1414 " (extra bits = " + paramBits + ")"); 1415 } 1416 inStream.skip(paramBits); 1417 bData.privacyIndicatorSet = decodeSuccess; 1418 return decodeSuccess; 1419 } 1420 1421 private static boolean decodeLanguageIndicator(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.language = inStream.read(8); 1431 } 1432 if ((! decodeSuccess) || (paramBits > 0)) { 1433 Log.d(LOG_TAG, "LANGUAGE_INDICATOR decode " + 1434 (decodeSuccess ? "succeeded" : "failed") + 1435 " (extra bits = " + paramBits + ")"); 1436 } 1437 inStream.skip(paramBits); 1438 bData.languageIndicatorSet = decodeSuccess; 1439 return decodeSuccess; 1440 } 1441 1442 private static boolean decodeDisplayMode(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.displayMode = inStream.read(2); 1452 inStream.skip(6); 1453 } 1454 if ((! decodeSuccess) || (paramBits > 0)) { 1455 Log.d(LOG_TAG, "DISPLAY_MODE decode " + 1456 (decodeSuccess ? "succeeded" : "failed") + 1457 " (extra bits = " + paramBits + ")"); 1458 } 1459 inStream.skip(paramBits); 1460 bData.displayModeSet = decodeSuccess; 1461 return decodeSuccess; 1462 } 1463 1464 private static boolean decodePriorityIndicator(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.priority = inStream.read(2); 1474 inStream.skip(6); 1475 } 1476 if ((! decodeSuccess) || (paramBits > 0)) { 1477 Log.d(LOG_TAG, "PRIORITY_INDICATOR decode " + 1478 (decodeSuccess ? "succeeded" : "failed") + 1479 " (extra bits = " + paramBits + ")"); 1480 } 1481 inStream.skip(paramBits); 1482 bData.priorityIndicatorSet = decodeSuccess; 1483 return decodeSuccess; 1484 } 1485 1486 private static boolean decodeMsgDeliveryAlert(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.alert = inStream.read(2); 1496 inStream.skip(6); 1497 } 1498 if ((! decodeSuccess) || (paramBits > 0)) { 1499 Log.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " + 1500 (decodeSuccess ? "succeeded" : "failed") + 1501 " (extra bits = " + paramBits + ")"); 1502 } 1503 inStream.skip(paramBits); 1504 bData.alertIndicatorSet = decodeSuccess; 1505 return decodeSuccess; 1506 } 1507 1508 private static boolean decodeUserResponseCode(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.userResponseCode = inStream.read(8); 1518 } 1519 if ((! decodeSuccess) || (paramBits > 0)) { 1520 Log.d(LOG_TAG, "USER_REPONSE_CODE decode " + 1521 (decodeSuccess ? "succeeded" : "failed") + 1522 " (extra bits = " + paramBits + ")"); 1523 } 1524 inStream.skip(paramBits); 1525 bData.userResponseCodeSet = decodeSuccess; 1526 return decodeSuccess; 1527 } 1528 1529 /** 1530 * Create BearerData object from serialized representation. 1531 * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details) 1532 * 1533 * @param smsData byte array of raw encoded SMS bearer data. 1534 * 1535 * @return an instance of BearerData. 1536 */ 1537 public static BearerData decode(byte[] smsData) { 1538 try { 1539 BitwiseInputStream inStream = new BitwiseInputStream(smsData); 1540 BearerData bData = new BearerData(); 1541 int foundSubparamMask = 0; 1542 while (inStream.available() > 0) { 1543 boolean decodeSuccess = false; 1544 int subparamId = inStream.read(8); 1545 int subparamIdBit = 1 << subparamId; 1546 if ((foundSubparamMask & subparamIdBit) != 0) { 1547 throw new CodingException("illegal duplicate subparameter (" + 1548 subparamId + ")"); 1549 } 1550 switch (subparamId) { 1551 case SUBPARAM_MESSAGE_IDENTIFIER: 1552 decodeSuccess = decodeMessageId(bData, inStream); 1553 break; 1554 case SUBPARAM_USER_DATA: 1555 decodeSuccess = decodeUserData(bData, inStream); 1556 break; 1557 case SUBPARAM_USER_REPONSE_CODE: 1558 decodeSuccess = decodeUserResponseCode(bData, inStream); 1559 break; 1560 case SUBPARAM_REPLY_OPTION: 1561 decodeSuccess = decodeReplyOption(bData, inStream); 1562 break; 1563 case SUBPARAM_NUMBER_OF_MESSAGES: 1564 decodeSuccess = decodeMsgCount(bData, inStream); 1565 break; 1566 case SUBPARAM_CALLBACK_NUMBER: 1567 decodeSuccess = decodeCallbackNumber(bData, inStream); 1568 break; 1569 case SUBPARAM_MESSAGE_STATUS: 1570 decodeSuccess = decodeMsgStatus(bData, inStream); 1571 break; 1572 case SUBPARAM_MESSAGE_CENTER_TIME_STAMP: 1573 decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream); 1574 break; 1575 case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE: 1576 decodeSuccess = decodeValidityAbs(bData, inStream); 1577 break; 1578 case SUBPARAM_VALIDITY_PERIOD_RELATIVE: 1579 decodeSuccess = decodeValidityRel(bData, inStream); 1580 break; 1581 case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE: 1582 decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream); 1583 break; 1584 case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE: 1585 decodeSuccess = decodeDeferredDeliveryRel(bData, inStream); 1586 break; 1587 case SUBPARAM_PRIVACY_INDICATOR: 1588 decodeSuccess = decodePrivacyIndicator(bData, inStream); 1589 break; 1590 case SUBPARAM_LANGUAGE_INDICATOR: 1591 decodeSuccess = decodeLanguageIndicator(bData, inStream); 1592 break; 1593 case SUBPARAM_MESSAGE_DISPLAY_MODE: 1594 decodeSuccess = decodeDisplayMode(bData, inStream); 1595 break; 1596 case SUBPARAM_PRIORITY_INDICATOR: 1597 decodeSuccess = decodePriorityIndicator(bData, inStream); 1598 break; 1599 case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY: 1600 decodeSuccess = decodeMsgDeliveryAlert(bData, inStream); 1601 break; 1602 case SUBPARAM_MESSAGE_DEPOSIT_INDEX: 1603 decodeSuccess = decodeDepositIndex(bData, inStream); 1604 break; 1605 default: 1606 throw new CodingException("unsupported bearer data subparameter (" 1607 + subparamId + ")"); 1608 } 1609 if (decodeSuccess) foundSubparamMask |= subparamIdBit; 1610 } 1611 if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) { 1612 throw new CodingException("missing MESSAGE_IDENTIFIER subparam"); 1613 } 1614 if (bData.userData != null) { 1615 if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) { 1616 if ((foundSubparamMask ^ 1617 (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^ 1618 (1 << SUBPARAM_USER_DATA)) 1619 != 0) { 1620 Log.e(LOG_TAG, "IS-91 must occur without extra subparams (" + 1621 foundSubparamMask + ")"); 1622 } 1623 decodeIs91(bData); 1624 } else { 1625 decodeUserDataPayload(bData.userData, bData.hasUserDataHeader); 1626 } 1627 } 1628 return bData; 1629 } catch (BitwiseInputStream.AccessException ex) { 1630 Log.e(LOG_TAG, "BearerData decode failed: " + ex); 1631 } catch (CodingException ex) { 1632 Log.e(LOG_TAG, "BearerData decode failed: " + ex); 1633 } 1634 return null; 1635 } 1636 } 1637