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 android.content.res.Resources; 20 import android.telephony.SmsCbCmasInfo; 21 import android.telephony.cdma.CdmaSmsCbProgramData; 22 import android.telephony.cdma.CdmaSmsCbProgramResults; 23 import android.text.format.Time; 24 import android.telephony.Rlog; 25 26 import com.android.internal.telephony.GsmAlphabet; 27 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; 28 import com.android.internal.telephony.SmsConstants; 29 import com.android.internal.telephony.SmsHeader; 30 import com.android.internal.telephony.SmsMessageBase; 31 import com.android.internal.telephony.uicc.IccUtils; 32 import com.android.internal.util.BitwiseInputStream; 33 import com.android.internal.util.BitwiseOutputStream; 34 35 import java.util.ArrayList; 36 import java.util.TimeZone; 37 38 /** 39 * An object to encode and decode CDMA SMS bearer data. 40 */ 41 public final class BearerData { 42 private final static String LOG_TAG = "BearerData"; 43 44 /** 45 * Bearer Data Subparameter Identifiers 46 * (See 3GPP2 C.S0015-B, v2.0, table 4.5-1) 47 * NOTE: Commented subparameter types are not implemented. 48 */ 49 private final static byte SUBPARAM_MESSAGE_IDENTIFIER = 0x00; 50 private final static byte SUBPARAM_USER_DATA = 0x01; 51 private final static byte SUBPARAM_USER_RESPONSE_CODE = 0x02; 52 private final static byte SUBPARAM_MESSAGE_CENTER_TIME_STAMP = 0x03; 53 private final static byte SUBPARAM_VALIDITY_PERIOD_ABSOLUTE = 0x04; 54 private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE = 0x05; 55 private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE = 0x06; 56 private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE = 0x07; 57 private final static byte SUBPARAM_PRIORITY_INDICATOR = 0x08; 58 private final static byte SUBPARAM_PRIVACY_INDICATOR = 0x09; 59 private final static byte SUBPARAM_REPLY_OPTION = 0x0A; 60 private final static byte SUBPARAM_NUMBER_OF_MESSAGES = 0x0B; 61 private final static byte SUBPARAM_ALERT_ON_MESSAGE_DELIVERY = 0x0C; 62 private final static byte SUBPARAM_LANGUAGE_INDICATOR = 0x0D; 63 private final static byte SUBPARAM_CALLBACK_NUMBER = 0x0E; 64 private final static byte SUBPARAM_MESSAGE_DISPLAY_MODE = 0x0F; 65 //private final static byte SUBPARAM_MULTIPLE_ENCODING_USER_DATA = 0x10; 66 private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX = 0x11; 67 private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA = 0x12; 68 private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS = 0x13; 69 private final static byte SUBPARAM_MESSAGE_STATUS = 0x14; 70 //private final static byte SUBPARAM_TP_FAILURE_CAUSE = 0x15; 71 //private final static byte SUBPARAM_ENHANCED_VMN = 0x16; 72 //private final static byte SUBPARAM_ENHANCED_VMN_ACK = 0x17; 73 74 // All other values after this are reserved. 75 private final static byte SUBPARAM_ID_LAST_DEFINED = 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 TimeStamp()233 public TimeStamp() { 234 super(TimeZone.getDefault().getID()); // 3GPP2 timestamps use the local timezone 235 } 236 fromByteArray(byte[] data)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 toString()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 /** 342 * CMAS warning notification information. 343 * @see #decodeCmasUserData(BearerData, int) 344 */ 345 public SmsCbCmasInfo cmasWarningInfo; 346 347 /** 348 * The Service Category Program Data subparameter is used to enable and disable 349 * SMS broadcast service categories to display. If this subparameter is present, 350 * this field will contain a list of one or more 351 * {@link android.telephony.cdma.CdmaSmsCbProgramData} objects containing the 352 * operation(s) to perform. 353 */ 354 public ArrayList<CdmaSmsCbProgramData> serviceCategoryProgramData; 355 356 /** 357 * The Service Category Program Results subparameter informs the message center 358 * of the results of a Service Category Program Data request. 359 */ 360 public ArrayList<CdmaSmsCbProgramResults> serviceCategoryProgramResults; 361 362 363 private static class CodingException extends Exception { CodingException(String s)364 public CodingException(String s) { 365 super(s); 366 } 367 } 368 369 /** 370 * Returns the language indicator as a two-character ISO 639 string. 371 * @return a two character ISO 639 language code 372 */ getLanguage()373 public String getLanguage() { 374 return getLanguageCodeForValue(language); 375 } 376 377 /** 378 * Converts a CDMA language indicator value to an ISO 639 two character language code. 379 * @param languageValue the CDMA language value to convert 380 * @return the two character ISO 639 language code for the specified value, or null if unknown 381 */ getLanguageCodeForValue(int languageValue)382 private static String getLanguageCodeForValue(int languageValue) { 383 switch (languageValue) { 384 case LANGUAGE_ENGLISH: 385 return "en"; 386 387 case LANGUAGE_FRENCH: 388 return "fr"; 389 390 case LANGUAGE_SPANISH: 391 return "es"; 392 393 case LANGUAGE_JAPANESE: 394 return "ja"; 395 396 case LANGUAGE_KOREAN: 397 return "ko"; 398 399 case LANGUAGE_CHINESE: 400 return "zh"; 401 402 case LANGUAGE_HEBREW: 403 return "he"; 404 405 default: 406 return null; 407 } 408 } 409 410 @Override toString()411 public String toString() { 412 StringBuilder builder = new StringBuilder(); 413 builder.append("BearerData "); 414 builder.append("{ messageType=" + messageType); 415 builder.append(", messageId=" + messageId); 416 builder.append(", priority=" + (priorityIndicatorSet ? priority : "unset")); 417 builder.append(", privacy=" + (privacyIndicatorSet ? privacy : "unset")); 418 builder.append(", alert=" + (alertIndicatorSet ? alert : "unset")); 419 builder.append(", displayMode=" + (displayModeSet ? displayMode : "unset")); 420 builder.append(", language=" + (languageIndicatorSet ? language : "unset")); 421 builder.append(", errorClass=" + (messageStatusSet ? errorClass : "unset")); 422 builder.append(", msgStatus=" + (messageStatusSet ? messageStatus : "unset")); 423 builder.append(", msgCenterTimeStamp=" + 424 ((msgCenterTimeStamp != null) ? msgCenterTimeStamp : "unset")); 425 builder.append(", validityPeriodAbsolute=" + 426 ((validityPeriodAbsolute != null) ? validityPeriodAbsolute : "unset")); 427 builder.append(", validityPeriodRelative=" + 428 ((validityPeriodRelativeSet) ? validityPeriodRelative : "unset")); 429 builder.append(", deferredDeliveryTimeAbsolute=" + 430 ((deferredDeliveryTimeAbsolute != null) ? deferredDeliveryTimeAbsolute : "unset")); 431 builder.append(", deferredDeliveryTimeRelative=" + 432 ((deferredDeliveryTimeRelativeSet) ? deferredDeliveryTimeRelative : "unset")); 433 builder.append(", userAckReq=" + userAckReq); 434 builder.append(", deliveryAckReq=" + deliveryAckReq); 435 builder.append(", readAckReq=" + readAckReq); 436 builder.append(", reportReq=" + reportReq); 437 builder.append(", numberOfMessages=" + numberOfMessages); 438 builder.append(", callbackNumber=" + Rlog.pii(LOG_TAG, callbackNumber)); 439 builder.append(", depositIndex=" + depositIndex); 440 builder.append(", hasUserDataHeader=" + hasUserDataHeader); 441 builder.append(", userData=" + userData); 442 builder.append(" }"); 443 return builder.toString(); 444 } 445 encodeMessageId(BearerData bData, BitwiseOutputStream outStream)446 private static void encodeMessageId(BearerData bData, BitwiseOutputStream outStream) 447 throws BitwiseOutputStream.AccessException 448 { 449 outStream.write(8, 3); 450 outStream.write(4, bData.messageType); 451 outStream.write(8, bData.messageId >> 8); 452 outStream.write(8, bData.messageId); 453 outStream.write(1, bData.hasUserDataHeader ? 1 : 0); 454 outStream.skip(3); 455 } 456 countAsciiSeptets(CharSequence msg, boolean force)457 private static int countAsciiSeptets(CharSequence msg, boolean force) { 458 int msgLen = msg.length(); 459 if (force) return msgLen; 460 for (int i = 0; i < msgLen; i++) { 461 if (UserData.charToAscii.get(msg.charAt(i), -1) == -1) { 462 return -1; 463 } 464 } 465 return msgLen; 466 } 467 468 /** 469 * Calculate the message text encoding length, fragmentation, and other details. 470 * 471 * @param msg message text 472 * @param force7BitEncoding ignore (but still count) illegal characters if true 473 * @param isEntireMsg indicates if this is entire msg or a segment in multipart msg 474 * @return septet count, or -1 on failure 475 */ calcTextEncodingDetails(CharSequence msg, boolean force7BitEncoding, boolean isEntireMsg)476 public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg, 477 boolean force7BitEncoding, boolean isEntireMsg) { 478 TextEncodingDetails ted; 479 int septets = countAsciiSeptets(msg, force7BitEncoding); 480 if (septets != -1 && septets <= SmsConstants.MAX_USER_DATA_SEPTETS) { 481 ted = new TextEncodingDetails(); 482 ted.msgCount = 1; 483 ted.codeUnitCount = septets; 484 ted.codeUnitsRemaining = SmsConstants.MAX_USER_DATA_SEPTETS - septets; 485 ted.codeUnitSize = SmsConstants.ENCODING_7BIT; 486 } else { 487 ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength( 488 msg, force7BitEncoding); 489 if (ted.msgCount == 1 && ted.codeUnitSize == SmsConstants.ENCODING_7BIT && 490 isEntireMsg) { 491 // We don't support single-segment EMS, so calculate for 16-bit 492 // TODO: Consider supporting single-segment EMS 493 return SmsMessageBase.calcUnicodeEncodingDetails(msg); 494 } 495 } 496 return ted; 497 } 498 encode7bitAscii(String msg, boolean force)499 private static byte[] encode7bitAscii(String msg, boolean force) 500 throws CodingException 501 { 502 try { 503 BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length()); 504 int msgLen = msg.length(); 505 for (int i = 0; i < msgLen; i++) { 506 int charCode = UserData.charToAscii.get(msg.charAt(i), -1); 507 if (charCode == -1) { 508 if (force) { 509 outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR); 510 } else { 511 throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")"); 512 } 513 } else { 514 outStream.write(7, charCode); 515 } 516 } 517 return outStream.toByteArray(); 518 } catch (BitwiseOutputStream.AccessException ex) { 519 throw new CodingException("7bit ASCII encode failed: " + ex); 520 } 521 } 522 encodeUtf16(String msg)523 private static byte[] encodeUtf16(String msg) 524 throws CodingException 525 { 526 try { 527 return msg.getBytes("utf-16be"); 528 } catch (java.io.UnsupportedEncodingException ex) { 529 throw new CodingException("UTF-16 encode failed: " + ex); 530 } 531 } 532 533 private static class Gsm7bitCodingResult { 534 int septets; 535 byte[] data; 536 } 537 encode7bitGsm(String msg, int septetOffset, boolean force)538 private static Gsm7bitCodingResult encode7bitGsm(String msg, int septetOffset, boolean force) 539 throws CodingException 540 { 541 try { 542 /* 543 * TODO(cleanup): It would be nice if GsmAlphabet provided 544 * an option to produce just the data without prepending 545 * the septet count, as this function is really just a 546 * wrapper to strip that off. Not to mention that the 547 * septet count is generally known prior to invocation of 548 * the encoder. Note that it cannot be derived from the 549 * resulting array length, since that cannot distinguish 550 * if the last contains either 1 or 8 valid bits. 551 * 552 * TODO(cleanup): The BitwiseXStreams could also be 553 * extended with byte-wise reversed endianness read/write 554 * routines to allow a corresponding implementation of 555 * stringToGsm7BitPacked, and potentially directly support 556 * access to the main bitwise stream from encode/decode. 557 */ 558 byte[] fullData = GsmAlphabet.stringToGsm7BitPacked(msg, septetOffset, !force, 0, 0); 559 Gsm7bitCodingResult result = new Gsm7bitCodingResult(); 560 result.data = new byte[fullData.length - 1]; 561 System.arraycopy(fullData, 1, result.data, 0, fullData.length - 1); 562 result.septets = fullData[0] & 0x00FF; 563 return result; 564 } catch (com.android.internal.telephony.EncodeException ex) { 565 throw new CodingException("7bit GSM encode failed: " + ex); 566 } 567 } 568 encode7bitEms(UserData uData, byte[] udhData, boolean force)569 private static void encode7bitEms(UserData uData, byte[] udhData, boolean force) 570 throws CodingException 571 { 572 int udhBytes = udhData.length + 1; // Add length octet. 573 int udhSeptets = ((udhBytes * 8) + 6) / 7; 574 Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, udhSeptets, force); 575 uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET; 576 uData.msgEncodingSet = true; 577 uData.numFields = gcr.septets; 578 uData.payload = gcr.data; 579 uData.payload[0] = (byte)udhData.length; 580 System.arraycopy(udhData, 0, uData.payload, 1, udhData.length); 581 } 582 encode16bitEms(UserData uData, byte[] udhData)583 private static void encode16bitEms(UserData uData, byte[] udhData) 584 throws CodingException 585 { 586 byte[] payload = encodeUtf16(uData.payloadStr); 587 int udhBytes = udhData.length + 1; // Add length octet. 588 int udhCodeUnits = (udhBytes + 1) / 2; 589 int payloadCodeUnits = payload.length / 2; 590 uData.msgEncoding = UserData.ENCODING_UNICODE_16; 591 uData.msgEncodingSet = true; 592 uData.numFields = udhCodeUnits + payloadCodeUnits; 593 uData.payload = new byte[uData.numFields * 2]; 594 uData.payload[0] = (byte)udhData.length; 595 System.arraycopy(udhData, 0, uData.payload, 1, udhData.length); 596 System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length); 597 } 598 encode7bitAsciiEms(UserData uData, byte[] udhData, boolean force)599 private static void encode7bitAsciiEms(UserData uData, byte[] udhData, boolean force) 600 throws CodingException 601 { 602 try { 603 Rlog.d(LOG_TAG, "encode7bitAsciiEms"); 604 int udhBytes = udhData.length + 1; // Add length octet. 605 int udhSeptets = ((udhBytes * 8) + 6) / 7; 606 int paddingBits = (udhSeptets * 7) - (udhBytes * 8); 607 String msg = uData.payloadStr; 608 byte[] payload ; 609 int msgLen = msg.length(); 610 BitwiseOutputStream outStream = new BitwiseOutputStream(msgLen + 611 (paddingBits > 0 ? 1 : 0)); 612 outStream.write(paddingBits, 0); 613 for (int i = 0; i < msgLen; i++) { 614 int charCode = UserData.charToAscii.get(msg.charAt(i), -1); 615 if (charCode == -1) { 616 if (force) { 617 outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR); 618 } else { 619 throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")"); 620 } 621 } else { 622 outStream.write(7, charCode); 623 } 624 } 625 payload = outStream.toByteArray(); 626 uData.msgEncoding = UserData.ENCODING_7BIT_ASCII; 627 uData.msgEncodingSet = true; 628 uData.numFields = udhSeptets + uData.payloadStr.length(); 629 uData.payload = new byte[udhBytes + payload.length]; 630 uData.payload[0] = (byte)udhData.length; 631 System.arraycopy(udhData, 0, uData.payload, 1, udhData.length); 632 System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length); 633 } catch (BitwiseOutputStream.AccessException ex) { 634 throw new CodingException("7bit ASCII encode failed: " + ex); 635 } 636 } 637 encodeEmsUserDataPayload(UserData uData)638 private static void encodeEmsUserDataPayload(UserData uData) 639 throws CodingException 640 { 641 byte[] headerData = SmsHeader.toByteArray(uData.userDataHeader); 642 if (uData.msgEncodingSet) { 643 if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) { 644 encode7bitEms(uData, headerData, true); 645 } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) { 646 encode16bitEms(uData, headerData); 647 } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) { 648 encode7bitAsciiEms(uData, headerData, true); 649 } else { 650 throw new CodingException("unsupported EMS user data encoding (" + 651 uData.msgEncoding + ")"); 652 } 653 } else { 654 try { 655 encode7bitEms(uData, headerData, false); 656 } catch (CodingException ex) { 657 encode16bitEms(uData, headerData); 658 } 659 } 660 } 661 encodeShiftJis(String msg)662 private static byte[] encodeShiftJis(String msg) throws CodingException { 663 try { 664 return msg.getBytes("Shift_JIS"); 665 } catch (java.io.UnsupportedEncodingException ex) { 666 throw new CodingException("Shift-JIS encode failed: " + ex); 667 } 668 } 669 encodeUserDataPayload(UserData uData)670 private static void encodeUserDataPayload(UserData uData) 671 throws CodingException 672 { 673 if ((uData.payloadStr == null) && (uData.msgEncoding != UserData.ENCODING_OCTET)) { 674 Rlog.e(LOG_TAG, "user data with null payloadStr"); 675 uData.payloadStr = ""; 676 } 677 678 if (uData.userDataHeader != null) { 679 encodeEmsUserDataPayload(uData); 680 return; 681 } 682 683 if (uData.msgEncodingSet) { 684 if (uData.msgEncoding == UserData.ENCODING_OCTET) { 685 if (uData.payload == null) { 686 Rlog.e(LOG_TAG, "user data with octet encoding but null payload"); 687 uData.payload = new byte[0]; 688 uData.numFields = 0; 689 } else { 690 uData.numFields = uData.payload.length; 691 } 692 } else { 693 if (uData.payloadStr == null) { 694 Rlog.e(LOG_TAG, "non-octet user data with null payloadStr"); 695 uData.payloadStr = ""; 696 } 697 if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) { 698 Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, 0, true); 699 uData.payload = gcr.data; 700 uData.numFields = gcr.septets; 701 } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) { 702 uData.payload = encode7bitAscii(uData.payloadStr, true); 703 uData.numFields = uData.payloadStr.length(); 704 } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) { 705 uData.payload = encodeUtf16(uData.payloadStr); 706 uData.numFields = uData.payloadStr.length(); 707 } else if (uData.msgEncoding == UserData.ENCODING_SHIFT_JIS) { 708 uData.payload = encodeShiftJis(uData.payloadStr); 709 uData.numFields = uData.payload.length; 710 } else { 711 throw new CodingException("unsupported user data encoding (" + 712 uData.msgEncoding + ")"); 713 } 714 } 715 } else { 716 try { 717 uData.payload = encode7bitAscii(uData.payloadStr, false); 718 uData.msgEncoding = UserData.ENCODING_7BIT_ASCII; 719 } catch (CodingException ex) { 720 uData.payload = encodeUtf16(uData.payloadStr); 721 uData.msgEncoding = UserData.ENCODING_UNICODE_16; 722 } 723 uData.numFields = uData.payloadStr.length(); 724 uData.msgEncodingSet = true; 725 } 726 } 727 encodeUserData(BearerData bData, BitwiseOutputStream outStream)728 private static void encodeUserData(BearerData bData, BitwiseOutputStream outStream) 729 throws BitwiseOutputStream.AccessException, CodingException 730 { 731 /* 732 * TODO(cleanup): Do we really need to set userData.payload as 733 * a side effect of encoding? If not, we could avoid data 734 * copies by passing outStream directly. 735 */ 736 encodeUserDataPayload(bData.userData); 737 bData.hasUserDataHeader = bData.userData.userDataHeader != null; 738 739 if (bData.userData.payload.length > SmsConstants.MAX_USER_DATA_BYTES) { 740 throw new CodingException("encoded user data too large (" + 741 bData.userData.payload.length + 742 " > " + SmsConstants.MAX_USER_DATA_BYTES + " bytes)"); 743 } 744 745 /* 746 * TODO(cleanup): figure out what the right answer is WRT paddingBits field 747 * 748 * userData.paddingBits = (userData.payload.length * 8) - (userData.numFields * 7); 749 * userData.paddingBits = 0; // XXX this seems better, but why? 750 * 751 */ 752 int dataBits = (bData.userData.payload.length * 8) - bData.userData.paddingBits; 753 int paramBits = dataBits + 13; 754 if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) || 755 (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) { 756 paramBits += 8; 757 } 758 int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0); 759 int paddingBits = (paramBytes * 8) - paramBits; 760 outStream.write(8, paramBytes); 761 outStream.write(5, bData.userData.msgEncoding); 762 if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) || 763 (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) { 764 outStream.write(8, bData.userData.msgType); 765 } 766 outStream.write(8, bData.userData.numFields); 767 outStream.writeByteArray(dataBits, bData.userData.payload); 768 if (paddingBits > 0) outStream.write(paddingBits, 0); 769 } 770 encodeReplyOption(BearerData bData, BitwiseOutputStream outStream)771 private static void encodeReplyOption(BearerData bData, BitwiseOutputStream outStream) 772 throws BitwiseOutputStream.AccessException 773 { 774 outStream.write(8, 1); 775 outStream.write(1, bData.userAckReq ? 1 : 0); 776 outStream.write(1, bData.deliveryAckReq ? 1 : 0); 777 outStream.write(1, bData.readAckReq ? 1 : 0); 778 outStream.write(1, bData.reportReq ? 1 : 0); 779 outStream.write(4, 0); 780 } 781 encodeDtmfSmsAddress(String address)782 private static byte[] encodeDtmfSmsAddress(String address) { 783 int digits = address.length(); 784 int dataBits = digits * 4; 785 int dataBytes = (dataBits / 8); 786 dataBytes += (dataBits % 8) > 0 ? 1 : 0; 787 byte[] rawData = new byte[dataBytes]; 788 for (int i = 0; i < digits; i++) { 789 char c = address.charAt(i); 790 int val = 0; 791 if ((c >= '1') && (c <= '9')) val = c - '0'; 792 else if (c == '0') val = 10; 793 else if (c == '*') val = 11; 794 else if (c == '#') val = 12; 795 else return null; 796 rawData[i / 2] |= val << (4 - ((i % 2) * 4)); 797 } 798 return rawData; 799 } 800 801 /* 802 * TODO(cleanup): CdmaSmsAddress encoding should make use of 803 * CdmaSmsAddress.parse provided that DTMF encoding is unified, 804 * and the difference in 4-bit vs. 8-bit is resolved. 805 */ 806 encodeCdmaSmsAddress(CdmaSmsAddress addr)807 private static void encodeCdmaSmsAddress(CdmaSmsAddress addr) throws CodingException { 808 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 809 try { 810 addr.origBytes = addr.address.getBytes("US-ASCII"); 811 } catch (java.io.UnsupportedEncodingException ex) { 812 throw new CodingException("invalid SMS address, cannot convert to ASCII"); 813 } 814 } else { 815 addr.origBytes = encodeDtmfSmsAddress(addr.address); 816 } 817 } 818 encodeCallbackNumber(BearerData bData, BitwiseOutputStream outStream)819 private static void encodeCallbackNumber(BearerData bData, BitwiseOutputStream outStream) 820 throws BitwiseOutputStream.AccessException, CodingException 821 { 822 CdmaSmsAddress addr = bData.callbackNumber; 823 encodeCdmaSmsAddress(addr); 824 int paramBits = 9; 825 int dataBits = 0; 826 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 827 paramBits += 7; 828 dataBits = addr.numberOfDigits * 8; 829 } else { 830 dataBits = addr.numberOfDigits * 4; 831 } 832 paramBits += dataBits; 833 int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0); 834 int paddingBits = (paramBytes * 8) - paramBits; 835 outStream.write(8, paramBytes); 836 outStream.write(1, addr.digitMode); 837 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 838 outStream.write(3, addr.ton); 839 outStream.write(4, addr.numberPlan); 840 } 841 outStream.write(8, addr.numberOfDigits); 842 outStream.writeByteArray(dataBits, addr.origBytes); 843 if (paddingBits > 0) outStream.write(paddingBits, 0); 844 } 845 encodeMsgStatus(BearerData bData, BitwiseOutputStream outStream)846 private static void encodeMsgStatus(BearerData bData, BitwiseOutputStream outStream) 847 throws BitwiseOutputStream.AccessException 848 { 849 outStream.write(8, 1); 850 outStream.write(2, bData.errorClass); 851 outStream.write(6, bData.messageStatus); 852 } 853 encodeMsgCount(BearerData bData, BitwiseOutputStream outStream)854 private static void encodeMsgCount(BearerData bData, BitwiseOutputStream outStream) 855 throws BitwiseOutputStream.AccessException 856 { 857 outStream.write(8, 1); 858 outStream.write(8, bData.numberOfMessages); 859 } 860 encodeValidityPeriodRel(BearerData bData, BitwiseOutputStream outStream)861 private static void encodeValidityPeriodRel(BearerData bData, BitwiseOutputStream outStream) 862 throws BitwiseOutputStream.AccessException 863 { 864 outStream.write(8, 1); 865 outStream.write(8, bData.validityPeriodRelative); 866 } 867 encodePrivacyIndicator(BearerData bData, BitwiseOutputStream outStream)868 private static void encodePrivacyIndicator(BearerData bData, BitwiseOutputStream outStream) 869 throws BitwiseOutputStream.AccessException 870 { 871 outStream.write(8, 1); 872 outStream.write(2, bData.privacy); 873 outStream.skip(6); 874 } 875 encodeLanguageIndicator(BearerData bData, BitwiseOutputStream outStream)876 private static void encodeLanguageIndicator(BearerData bData, BitwiseOutputStream outStream) 877 throws BitwiseOutputStream.AccessException 878 { 879 outStream.write(8, 1); 880 outStream.write(8, bData.language); 881 } 882 encodeDisplayMode(BearerData bData, BitwiseOutputStream outStream)883 private static void encodeDisplayMode(BearerData bData, BitwiseOutputStream outStream) 884 throws BitwiseOutputStream.AccessException 885 { 886 outStream.write(8, 1); 887 outStream.write(2, bData.displayMode); 888 outStream.skip(6); 889 } 890 encodePriorityIndicator(BearerData bData, BitwiseOutputStream outStream)891 private static void encodePriorityIndicator(BearerData bData, BitwiseOutputStream outStream) 892 throws BitwiseOutputStream.AccessException 893 { 894 outStream.write(8, 1); 895 outStream.write(2, bData.priority); 896 outStream.skip(6); 897 } 898 encodeMsgDeliveryAlert(BearerData bData, BitwiseOutputStream outStream)899 private static void encodeMsgDeliveryAlert(BearerData bData, BitwiseOutputStream outStream) 900 throws BitwiseOutputStream.AccessException 901 { 902 outStream.write(8, 1); 903 outStream.write(2, bData.alert); 904 outStream.skip(6); 905 } 906 encodeScpResults(BearerData bData, BitwiseOutputStream outStream)907 private static void encodeScpResults(BearerData bData, BitwiseOutputStream outStream) 908 throws BitwiseOutputStream.AccessException 909 { 910 ArrayList<CdmaSmsCbProgramResults> results = bData.serviceCategoryProgramResults; 911 outStream.write(8, (results.size() * 4)); // 4 octets per program result 912 for (CdmaSmsCbProgramResults result : results) { 913 int category = result.getCategory(); 914 outStream.write(8, category >> 8); 915 outStream.write(8, category); 916 outStream.write(8, result.getLanguage()); 917 outStream.write(4, result.getCategoryResult()); 918 outStream.skip(4); 919 } 920 } 921 922 /** 923 * Create serialized representation for BearerData object. 924 * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details) 925 * 926 * @param bData an instance of BearerData. 927 * 928 * @return byte array of raw encoded SMS bearer data. 929 */ encode(BearerData bData)930 public static byte[] encode(BearerData bData) { 931 bData.hasUserDataHeader = ((bData.userData != null) && 932 (bData.userData.userDataHeader != null)); 933 try { 934 BitwiseOutputStream outStream = new BitwiseOutputStream(200); 935 outStream.write(8, SUBPARAM_MESSAGE_IDENTIFIER); 936 encodeMessageId(bData, outStream); 937 if (bData.userData != null) { 938 outStream.write(8, SUBPARAM_USER_DATA); 939 encodeUserData(bData, outStream); 940 } 941 if (bData.callbackNumber != null) { 942 outStream.write(8, SUBPARAM_CALLBACK_NUMBER); 943 encodeCallbackNumber(bData, outStream); 944 } 945 if (bData.userAckReq || bData.deliveryAckReq || bData.readAckReq || bData.reportReq) { 946 outStream.write(8, SUBPARAM_REPLY_OPTION); 947 encodeReplyOption(bData, outStream); 948 } 949 if (bData.numberOfMessages != 0) { 950 outStream.write(8, SUBPARAM_NUMBER_OF_MESSAGES); 951 encodeMsgCount(bData, outStream); 952 } 953 if (bData.validityPeriodRelativeSet) { 954 outStream.write(8, SUBPARAM_VALIDITY_PERIOD_RELATIVE); 955 encodeValidityPeriodRel(bData, outStream); 956 } 957 if (bData.privacyIndicatorSet) { 958 outStream.write(8, SUBPARAM_PRIVACY_INDICATOR); 959 encodePrivacyIndicator(bData, outStream); 960 } 961 if (bData.languageIndicatorSet) { 962 outStream.write(8, SUBPARAM_LANGUAGE_INDICATOR); 963 encodeLanguageIndicator(bData, outStream); 964 } 965 if (bData.displayModeSet) { 966 outStream.write(8, SUBPARAM_MESSAGE_DISPLAY_MODE); 967 encodeDisplayMode(bData, outStream); 968 } 969 if (bData.priorityIndicatorSet) { 970 outStream.write(8, SUBPARAM_PRIORITY_INDICATOR); 971 encodePriorityIndicator(bData, outStream); 972 } 973 if (bData.alertIndicatorSet) { 974 outStream.write(8, SUBPARAM_ALERT_ON_MESSAGE_DELIVERY); 975 encodeMsgDeliveryAlert(bData, outStream); 976 } 977 if (bData.messageStatusSet) { 978 outStream.write(8, SUBPARAM_MESSAGE_STATUS); 979 encodeMsgStatus(bData, outStream); 980 } 981 if (bData.serviceCategoryProgramResults != null) { 982 outStream.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS); 983 encodeScpResults(bData, outStream); 984 } 985 return outStream.toByteArray(); 986 } catch (BitwiseOutputStream.AccessException ex) { 987 Rlog.e(LOG_TAG, "BearerData encode failed: " + ex); 988 } catch (CodingException ex) { 989 Rlog.e(LOG_TAG, "BearerData encode failed: " + ex); 990 } 991 return null; 992 } 993 decodeMessageId(BearerData bData, BitwiseInputStream inStream)994 private static boolean decodeMessageId(BearerData bData, BitwiseInputStream inStream) 995 throws BitwiseInputStream.AccessException { 996 final int EXPECTED_PARAM_SIZE = 3 * 8; 997 boolean decodeSuccess = false; 998 int paramBits = inStream.read(8) * 8; 999 if (paramBits >= EXPECTED_PARAM_SIZE) { 1000 paramBits -= EXPECTED_PARAM_SIZE; 1001 decodeSuccess = true; 1002 bData.messageType = inStream.read(4); 1003 bData.messageId = inStream.read(8) << 8; 1004 bData.messageId |= inStream.read(8); 1005 bData.hasUserDataHeader = (inStream.read(1) == 1); 1006 inStream.skip(3); 1007 } 1008 if ((! decodeSuccess) || (paramBits > 0)) { 1009 Rlog.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " + 1010 (decodeSuccess ? "succeeded" : "failed") + 1011 " (extra bits = " + paramBits + ")"); 1012 } 1013 inStream.skip(paramBits); 1014 return decodeSuccess; 1015 } 1016 decodeReserved( BearerData bData, BitwiseInputStream inStream, int subparamId)1017 private static boolean decodeReserved( 1018 BearerData bData, BitwiseInputStream inStream, int subparamId) 1019 throws BitwiseInputStream.AccessException, CodingException 1020 { 1021 boolean decodeSuccess = false; 1022 int subparamLen = inStream.read(8); // SUBPARAM_LEN 1023 int paramBits = subparamLen * 8; 1024 if (paramBits <= inStream.available()) { 1025 decodeSuccess = true; 1026 inStream.skip(paramBits); 1027 } 1028 Rlog.d(LOG_TAG, "RESERVED bearer data subparameter " + subparamId + " decode " 1029 + (decodeSuccess ? "succeeded" : "failed") + " (param bits = " + paramBits + ")"); 1030 if (!decodeSuccess) { 1031 throw new CodingException("RESERVED bearer data subparameter " + subparamId 1032 + " had invalid SUBPARAM_LEN " + subparamLen); 1033 } 1034 1035 return decodeSuccess; 1036 } 1037 decodeUserData(BearerData bData, BitwiseInputStream inStream)1038 private static boolean decodeUserData(BearerData bData, BitwiseInputStream inStream) 1039 throws BitwiseInputStream.AccessException 1040 { 1041 int paramBits = inStream.read(8) * 8; 1042 bData.userData = new UserData(); 1043 bData.userData.msgEncoding = inStream.read(5); 1044 bData.userData.msgEncodingSet = true; 1045 bData.userData.msgType = 0; 1046 int consumedBits = 5; 1047 if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) || 1048 (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) { 1049 bData.userData.msgType = inStream.read(8); 1050 consumedBits += 8; 1051 } 1052 bData.userData.numFields = inStream.read(8); 1053 consumedBits += 8; 1054 int dataBits = paramBits - consumedBits; 1055 bData.userData.payload = inStream.readByteArray(dataBits); 1056 return true; 1057 } 1058 decodeUtf8(byte[] data, int offset, int numFields)1059 private static String decodeUtf8(byte[] data, int offset, int numFields) 1060 throws CodingException 1061 { 1062 return decodeCharset(data, offset, numFields, 1, "UTF-8"); 1063 } 1064 decodeUtf16(byte[] data, int offset, int numFields)1065 private static String decodeUtf16(byte[] data, int offset, int numFields) 1066 throws CodingException 1067 { 1068 // Subtract header and possible padding byte (at end) from num fields. 1069 int padding = offset % 2; 1070 numFields -= (offset + padding) / 2; 1071 return decodeCharset(data, offset, numFields, 2, "utf-16be"); 1072 } 1073 decodeCharset(byte[] data, int offset, int numFields, int width, String charset)1074 private static String decodeCharset(byte[] data, int offset, int numFields, int width, 1075 String charset) throws CodingException 1076 { 1077 if (numFields < 0 || (numFields * width + offset) > data.length) { 1078 // Try to decode the max number of characters in payload 1079 int padding = offset % width; 1080 int maxNumFields = (data.length - offset - padding) / width; 1081 if (maxNumFields < 0) { 1082 throw new CodingException(charset + " decode failed: offset out of range"); 1083 } 1084 Rlog.e(LOG_TAG, charset + " decode error: offset = " + offset + " numFields = " 1085 + numFields + " data.length = " + data.length + " maxNumFields = " 1086 + maxNumFields); 1087 numFields = maxNumFields; 1088 } 1089 try { 1090 return new String(data, offset, numFields * width, charset); 1091 } catch (java.io.UnsupportedEncodingException ex) { 1092 throw new CodingException(charset + " decode failed: " + ex); 1093 } 1094 } 1095 decode7bitAscii(byte[] data, int offset, int numFields)1096 private static String decode7bitAscii(byte[] data, int offset, int numFields) 1097 throws CodingException 1098 { 1099 try { 1100 int offsetBits = offset * 8; 1101 int offsetSeptets = (offsetBits + 6) / 7; 1102 numFields -= offsetSeptets; 1103 1104 StringBuffer strBuf = new StringBuffer(numFields); 1105 BitwiseInputStream inStream = new BitwiseInputStream(data); 1106 int wantedBits = (offsetSeptets * 7) + (numFields * 7); 1107 if (inStream.available() < wantedBits) { 1108 throw new CodingException("insufficient data (wanted " + wantedBits + 1109 " bits, but only have " + inStream.available() + ")"); 1110 } 1111 inStream.skip(offsetSeptets * 7); 1112 for (int i = 0; i < numFields; i++) { 1113 int charCode = inStream.read(7); 1114 if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) && 1115 (charCode <= UserData.ASCII_MAP_MAX_INDEX)) { 1116 strBuf.append(UserData.ASCII_MAP[charCode - UserData.ASCII_MAP_BASE_INDEX]); 1117 } else if (charCode == UserData.ASCII_NL_INDEX) { 1118 strBuf.append('\n'); 1119 } else if (charCode == UserData.ASCII_CR_INDEX) { 1120 strBuf.append('\r'); 1121 } else { 1122 /* For other charCodes, they are unprintable, and so simply use SPACE. */ 1123 strBuf.append(' '); 1124 } 1125 } 1126 return strBuf.toString(); 1127 } catch (BitwiseInputStream.AccessException ex) { 1128 throw new CodingException("7bit ASCII decode failed: " + ex); 1129 } 1130 } 1131 decode7bitGsm(byte[] data, int offset, int numFields)1132 private static String decode7bitGsm(byte[] data, int offset, int numFields) 1133 throws CodingException 1134 { 1135 // Start reading from the next 7-bit aligned boundary after offset. 1136 int offsetBits = offset * 8; 1137 int offsetSeptets = (offsetBits + 6) / 7; 1138 numFields -= offsetSeptets; 1139 int paddingBits = (offsetSeptets * 7) - offsetBits; 1140 String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits, 1141 0, 0); 1142 if (result == null) { 1143 throw new CodingException("7bit GSM decoding failed"); 1144 } 1145 return result; 1146 } 1147 decodeLatin(byte[] data, int offset, int numFields)1148 private static String decodeLatin(byte[] data, int offset, int numFields) 1149 throws CodingException 1150 { 1151 return decodeCharset(data, offset, numFields, 1, "ISO-8859-1"); 1152 } 1153 decodeShiftJis(byte[] data, int offset, int numFields)1154 private static String decodeShiftJis(byte[] data, int offset, int numFields) 1155 throws CodingException 1156 { 1157 return decodeCharset(data, offset, numFields, 1, "Shift_JIS"); 1158 } 1159 decodeGsmDcs(byte[] data, int offset, int numFields, int msgType)1160 private static String decodeGsmDcs(byte[] data, int offset, int numFields, int msgType) 1161 throws CodingException 1162 { 1163 if ((msgType & 0xC0) != 0) { 1164 throw new CodingException("unsupported coding group (" 1165 + msgType + ")"); 1166 } 1167 1168 switch ((msgType >> 2) & 0x3) { 1169 case UserData.ENCODING_GSM_DCS_7BIT: 1170 return decode7bitGsm(data, offset, numFields); 1171 case UserData.ENCODING_GSM_DCS_8BIT: 1172 return decodeUtf8(data, offset, numFields); 1173 case UserData.ENCODING_GSM_DCS_16BIT: 1174 return decodeUtf16(data, offset, numFields); 1175 default: 1176 throw new CodingException("unsupported user msgType encoding (" 1177 + msgType + ")"); 1178 } 1179 } 1180 decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)1181 private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader) 1182 throws CodingException 1183 { 1184 int offset = 0; 1185 if (hasUserDataHeader) { 1186 int udhLen = userData.payload[0] & 0x00FF; 1187 offset += udhLen + 1; 1188 byte[] headerData = new byte[udhLen]; 1189 System.arraycopy(userData.payload, 1, headerData, 0, udhLen); 1190 userData.userDataHeader = SmsHeader.fromByteArray(headerData); 1191 } 1192 switch (userData.msgEncoding) { 1193 case UserData.ENCODING_OCTET: 1194 /* 1195 * Octet decoding depends on the carrier service. 1196 */ 1197 boolean decodingtypeUTF8 = Resources.getSystem() 1198 .getBoolean(com.android.internal.R.bool.config_sms_utf8_support); 1199 1200 // Strip off any padding bytes, meaning any differences between the length of the 1201 // array and the target length specified by numFields. This is to avoid any 1202 // confusion by code elsewhere that only considers the payload array length. 1203 byte[] payload = new byte[userData.numFields]; 1204 int copyLen = userData.numFields < userData.payload.length 1205 ? userData.numFields : userData.payload.length; 1206 1207 System.arraycopy(userData.payload, 0, payload, 0, copyLen); 1208 userData.payload = payload; 1209 1210 if (!decodingtypeUTF8) { 1211 // There are many devices in the market that send 8bit text sms (latin encoded) as 1212 // octet encoded. 1213 userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields); 1214 } else { 1215 userData.payloadStr = decodeUtf8(userData.payload, offset, userData.numFields); 1216 } 1217 break; 1218 1219 case UserData.ENCODING_IA5: 1220 case UserData.ENCODING_7BIT_ASCII: 1221 userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields); 1222 break; 1223 case UserData.ENCODING_UNICODE_16: 1224 userData.payloadStr = decodeUtf16(userData.payload, offset, userData.numFields); 1225 break; 1226 case UserData.ENCODING_GSM_7BIT_ALPHABET: 1227 userData.payloadStr = decode7bitGsm(userData.payload, offset, userData.numFields); 1228 break; 1229 case UserData.ENCODING_LATIN: 1230 userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields); 1231 break; 1232 case UserData.ENCODING_SHIFT_JIS: 1233 userData.payloadStr = decodeShiftJis(userData.payload, offset, userData.numFields); 1234 break; 1235 case UserData.ENCODING_GSM_DCS: 1236 userData.payloadStr = decodeGsmDcs(userData.payload, offset, 1237 userData.numFields, userData.msgType); 1238 break; 1239 default: 1240 throw new CodingException("unsupported user data encoding (" 1241 + userData.msgEncoding + ")"); 1242 } 1243 } 1244 1245 /** 1246 * IS-91 Voice Mail message decoding 1247 * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) 1248 * (For character encodings, see TIA/EIA/IS-91, Annex B) 1249 * 1250 * Protocol Summary: The user data payload may contain 3-14 1251 * characters. The first two characters are parsed as a number 1252 * and indicate the number of voicemails. The third character is 1253 * either a SPACE or '!' to indicate normal or urgent priority, 1254 * respectively. Any following characters are treated as normal 1255 * text user data payload. 1256 * 1257 * Note that the characters encoding is 6-bit packed. 1258 */ 1259 private static void decodeIs91VoicemailStatus(BearerData bData) 1260 throws BitwiseInputStream.AccessException, CodingException 1261 { 1262 BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); 1263 int dataLen = inStream.available() / 6; // 6-bit packed character encoding. 1264 int numFields = bData.userData.numFields; 1265 if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) { 1266 throw new CodingException("IS-91 voicemail status decoding failed"); 1267 } 1268 try { 1269 StringBuffer strbuf = new StringBuffer(dataLen); 1270 while (inStream.available() >= 6) { 1271 strbuf.append(UserData.ASCII_MAP[inStream.read(6)]); 1272 } 1273 String data = strbuf.toString(); 1274 bData.numberOfMessages = Integer.parseInt(data.substring(0, 2)); 1275 char prioCode = data.charAt(2); 1276 if (prioCode == ' ') { 1277 bData.priority = PRIORITY_NORMAL; 1278 } else if (prioCode == '!') { 1279 bData.priority = PRIORITY_URGENT; 1280 } else { 1281 throw new CodingException("IS-91 voicemail status decoding failed: " + 1282 "illegal priority setting (" + prioCode + ")"); 1283 } 1284 bData.priorityIndicatorSet = true; 1285 bData.userData.payloadStr = data.substring(3, numFields - 3); 1286 } catch (java.lang.NumberFormatException ex) { 1287 throw new CodingException("IS-91 voicemail status decoding failed: " + ex); 1288 } catch (java.lang.IndexOutOfBoundsException ex) { 1289 throw new CodingException("IS-91 voicemail status decoding failed: " + ex); 1290 } 1291 } 1292 1293 /** 1294 * IS-91 Short Message decoding 1295 * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) 1296 * (For character encodings, see TIA/EIA/IS-91, Annex B) 1297 * 1298 * Protocol Summary: The user data payload may contain 1-14 1299 * characters, which are treated as normal text user data payload. 1300 * Note that the characters encoding is 6-bit packed. 1301 */ 1302 private static void decodeIs91ShortMessage(BearerData bData) 1303 throws BitwiseInputStream.AccessException, CodingException 1304 { 1305 BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); 1306 int dataLen = inStream.available() / 6; // 6-bit packed character encoding. 1307 int numFields = bData.userData.numFields; 1308 // dataLen may be > 14 characters due to octet padding 1309 if ((numFields > 14) || (dataLen < numFields)) { 1310 throw new CodingException("IS-91 short message decoding failed"); 1311 } 1312 StringBuffer strbuf = new StringBuffer(dataLen); 1313 for (int i = 0; i < numFields; i++) { 1314 strbuf.append(UserData.ASCII_MAP[inStream.read(6)]); 1315 } 1316 bData.userData.payloadStr = strbuf.toString(); 1317 } 1318 1319 /** 1320 * IS-91 CLI message (callback number) decoding 1321 * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) 1322 * 1323 * Protocol Summary: The data payload may contain 1-32 digits, 1324 * encoded using standard 4-bit DTMF, which are treated as a 1325 * callback number. 1326 */ decodeIs91Cli(BearerData bData)1327 private static void decodeIs91Cli(BearerData bData) throws CodingException { 1328 BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); 1329 int dataLen = inStream.available() / 4; // 4-bit packed DTMF digit encoding. 1330 int numFields = bData.userData.numFields; 1331 if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) { 1332 throw new CodingException("IS-91 voicemail status decoding failed"); 1333 } 1334 CdmaSmsAddress addr = new CdmaSmsAddress(); 1335 addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF; 1336 addr.origBytes = bData.userData.payload; 1337 addr.numberOfDigits = (byte)numFields; 1338 decodeSmsAddress(addr); 1339 bData.callbackNumber = addr; 1340 } 1341 decodeIs91(BearerData bData)1342 private static void decodeIs91(BearerData bData) 1343 throws BitwiseInputStream.AccessException, CodingException 1344 { 1345 switch (bData.userData.msgType) { 1346 case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS: 1347 decodeIs91VoicemailStatus(bData); 1348 break; 1349 case UserData.IS91_MSG_TYPE_CLI: 1350 decodeIs91Cli(bData); 1351 break; 1352 case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL: 1353 case UserData.IS91_MSG_TYPE_SHORT_MESSAGE: 1354 decodeIs91ShortMessage(bData); 1355 break; 1356 default: 1357 throw new CodingException("unsupported IS-91 message type (" + 1358 bData.userData.msgType + ")"); 1359 } 1360 } 1361 decodeReplyOption(BearerData bData, BitwiseInputStream inStream)1362 private static boolean decodeReplyOption(BearerData bData, BitwiseInputStream inStream) 1363 throws BitwiseInputStream.AccessException { 1364 final int EXPECTED_PARAM_SIZE = 1 * 8; 1365 boolean decodeSuccess = false; 1366 int paramBits = inStream.read(8) * 8; 1367 if (paramBits >= EXPECTED_PARAM_SIZE) { 1368 paramBits -= EXPECTED_PARAM_SIZE; 1369 decodeSuccess = true; 1370 bData.userAckReq = (inStream.read(1) == 1); 1371 bData.deliveryAckReq = (inStream.read(1) == 1); 1372 bData.readAckReq = (inStream.read(1) == 1); 1373 bData.reportReq = (inStream.read(1) == 1); 1374 inStream.skip(4); 1375 } 1376 if ((! decodeSuccess) || (paramBits > 0)) { 1377 Rlog.d(LOG_TAG, "REPLY_OPTION decode " + 1378 (decodeSuccess ? "succeeded" : "failed") + 1379 " (extra bits = " + paramBits + ")"); 1380 } 1381 inStream.skip(paramBits); 1382 return decodeSuccess; 1383 } 1384 decodeMsgCount(BearerData bData, BitwiseInputStream inStream)1385 private static boolean decodeMsgCount(BearerData bData, BitwiseInputStream inStream) 1386 throws BitwiseInputStream.AccessException { 1387 final int EXPECTED_PARAM_SIZE = 1 * 8; 1388 boolean decodeSuccess = false; 1389 int paramBits = inStream.read(8) * 8; 1390 if (paramBits >= EXPECTED_PARAM_SIZE) { 1391 paramBits -= EXPECTED_PARAM_SIZE; 1392 decodeSuccess = true; 1393 bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8)); 1394 } 1395 if ((! decodeSuccess) || (paramBits > 0)) { 1396 Rlog.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " + 1397 (decodeSuccess ? "succeeded" : "failed") + 1398 " (extra bits = " + paramBits + ")"); 1399 } 1400 inStream.skip(paramBits); 1401 return decodeSuccess; 1402 } 1403 decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)1404 private static boolean decodeDepositIndex(BearerData bData, BitwiseInputStream inStream) 1405 throws BitwiseInputStream.AccessException { 1406 final int EXPECTED_PARAM_SIZE = 2 * 8; 1407 boolean decodeSuccess = false; 1408 int paramBits = inStream.read(8) * 8; 1409 if (paramBits >= EXPECTED_PARAM_SIZE) { 1410 paramBits -= EXPECTED_PARAM_SIZE; 1411 decodeSuccess = true; 1412 bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8); 1413 } 1414 if ((! decodeSuccess) || (paramBits > 0)) { 1415 Rlog.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " + 1416 (decodeSuccess ? "succeeded" : "failed") + 1417 " (extra bits = " + paramBits + ")"); 1418 } 1419 inStream.skip(paramBits); 1420 return decodeSuccess; 1421 } 1422 decodeDtmfSmsAddress(byte[] rawData, int numFields)1423 private static String decodeDtmfSmsAddress(byte[] rawData, int numFields) 1424 throws CodingException 1425 { 1426 /* DTMF 4-bit digit encoding, defined in at 1427 * 3GPP2 C.S005-D, v2.0, table 2.7.1.3.2.4-4 */ 1428 StringBuffer strBuf = new StringBuffer(numFields); 1429 for (int i = 0; i < numFields; i++) { 1430 int val = 0x0F & (rawData[i / 2] >>> (4 - ((i % 2) * 4))); 1431 if ((val >= 1) && (val <= 9)) strBuf.append(Integer.toString(val, 10)); 1432 else if (val == 10) strBuf.append('0'); 1433 else if (val == 11) strBuf.append('*'); 1434 else if (val == 12) strBuf.append('#'); 1435 else throw new CodingException("invalid SMS address DTMF code (" + val + ")"); 1436 } 1437 return strBuf.toString(); 1438 } 1439 decodeSmsAddress(CdmaSmsAddress addr)1440 private static void decodeSmsAddress(CdmaSmsAddress addr) throws CodingException { 1441 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 1442 try { 1443 /* As specified in 3GPP2 C.S0015-B, v2, 4.5.15 -- actually 1444 * just 7-bit ASCII encoding, with the MSB being zero. */ 1445 addr.address = new String(addr.origBytes, 0, addr.origBytes.length, "US-ASCII"); 1446 } catch (java.io.UnsupportedEncodingException ex) { 1447 throw new CodingException("invalid SMS address ASCII code"); 1448 } 1449 } else { 1450 addr.address = decodeDtmfSmsAddress(addr.origBytes, addr.numberOfDigits); 1451 } 1452 } 1453 decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)1454 private static boolean decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream) 1455 throws BitwiseInputStream.AccessException, CodingException 1456 { 1457 final int EXPECTED_PARAM_SIZE = 1 * 8; //at least 1458 int paramBits = inStream.read(8) * 8; 1459 if (paramBits < EXPECTED_PARAM_SIZE) { 1460 inStream.skip(paramBits); 1461 return false; 1462 } 1463 CdmaSmsAddress addr = new CdmaSmsAddress(); 1464 addr.digitMode = inStream.read(1); 1465 byte fieldBits = 4; 1466 byte consumedBits = 1; 1467 if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) { 1468 addr.ton = inStream.read(3); 1469 addr.numberPlan = inStream.read(4); 1470 fieldBits = 8; 1471 consumedBits += 7; 1472 } 1473 addr.numberOfDigits = inStream.read(8); 1474 consumedBits += 8; 1475 int remainingBits = paramBits - consumedBits; 1476 int dataBits = addr.numberOfDigits * fieldBits; 1477 int paddingBits = remainingBits - dataBits; 1478 if (remainingBits < dataBits) { 1479 throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" + 1480 "remainingBits + " + remainingBits + ", dataBits + " + 1481 dataBits + ", paddingBits + " + paddingBits + ")"); 1482 } 1483 addr.origBytes = inStream.readByteArray(dataBits); 1484 inStream.skip(paddingBits); 1485 decodeSmsAddress(addr); 1486 bData.callbackNumber = addr; 1487 return true; 1488 } 1489 decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)1490 private static boolean decodeMsgStatus(BearerData bData, BitwiseInputStream inStream) 1491 throws BitwiseInputStream.AccessException { 1492 final int EXPECTED_PARAM_SIZE = 1 * 8; 1493 boolean decodeSuccess = false; 1494 int paramBits = inStream.read(8) * 8; 1495 if (paramBits >= EXPECTED_PARAM_SIZE) { 1496 paramBits -= EXPECTED_PARAM_SIZE; 1497 decodeSuccess = true; 1498 bData.errorClass = inStream.read(2); 1499 bData.messageStatus = inStream.read(6); 1500 } 1501 if ((! decodeSuccess) || (paramBits > 0)) { 1502 Rlog.d(LOG_TAG, "MESSAGE_STATUS decode " + 1503 (decodeSuccess ? "succeeded" : "failed") + 1504 " (extra bits = " + paramBits + ")"); 1505 } 1506 inStream.skip(paramBits); 1507 bData.messageStatusSet = decodeSuccess; 1508 return decodeSuccess; 1509 } 1510 decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)1511 private static boolean decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream) 1512 throws BitwiseInputStream.AccessException { 1513 final int EXPECTED_PARAM_SIZE = 6 * 8; 1514 boolean decodeSuccess = false; 1515 int paramBits = inStream.read(8) * 8; 1516 if (paramBits >= EXPECTED_PARAM_SIZE) { 1517 paramBits -= EXPECTED_PARAM_SIZE; 1518 decodeSuccess = true; 1519 bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8)); 1520 } 1521 if ((! decodeSuccess) || (paramBits > 0)) { 1522 Rlog.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " + 1523 (decodeSuccess ? "succeeded" : "failed") + 1524 " (extra bits = " + paramBits + ")"); 1525 } 1526 inStream.skip(paramBits); 1527 return decodeSuccess; 1528 } 1529 decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)1530 private static boolean decodeValidityAbs(BearerData bData, BitwiseInputStream inStream) 1531 throws BitwiseInputStream.AccessException { 1532 final int EXPECTED_PARAM_SIZE = 6 * 8; 1533 boolean decodeSuccess = false; 1534 int paramBits = inStream.read(8) * 8; 1535 if (paramBits >= EXPECTED_PARAM_SIZE) { 1536 paramBits -= EXPECTED_PARAM_SIZE; 1537 decodeSuccess = true; 1538 bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8)); 1539 } 1540 if ((! decodeSuccess) || (paramBits > 0)) { 1541 Rlog.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " + 1542 (decodeSuccess ? "succeeded" : "failed") + 1543 " (extra bits = " + paramBits + ")"); 1544 } 1545 inStream.skip(paramBits); 1546 return decodeSuccess; 1547 } 1548 decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)1549 private static boolean decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream) 1550 throws BitwiseInputStream.AccessException { 1551 final int EXPECTED_PARAM_SIZE = 6 * 8; 1552 boolean decodeSuccess = false; 1553 int paramBits = inStream.read(8) * 8; 1554 if (paramBits >= EXPECTED_PARAM_SIZE) { 1555 paramBits -= EXPECTED_PARAM_SIZE; 1556 decodeSuccess = true; 1557 bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray( 1558 inStream.readByteArray(6 * 8)); 1559 } 1560 if ((! decodeSuccess) || (paramBits > 0)) { 1561 Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " + 1562 (decodeSuccess ? "succeeded" : "failed") + 1563 " (extra bits = " + paramBits + ")"); 1564 } 1565 inStream.skip(paramBits); 1566 return decodeSuccess; 1567 } 1568 decodeValidityRel(BearerData bData, BitwiseInputStream inStream)1569 private static boolean decodeValidityRel(BearerData bData, BitwiseInputStream inStream) 1570 throws BitwiseInputStream.AccessException { 1571 final int EXPECTED_PARAM_SIZE = 1 * 8; 1572 boolean decodeSuccess = false; 1573 int paramBits = inStream.read(8) * 8; 1574 if (paramBits >= EXPECTED_PARAM_SIZE) { 1575 paramBits -= EXPECTED_PARAM_SIZE; 1576 decodeSuccess = true; 1577 bData.deferredDeliveryTimeRelative = inStream.read(8); 1578 } 1579 if ((! decodeSuccess) || (paramBits > 0)) { 1580 Rlog.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " + 1581 (decodeSuccess ? "succeeded" : "failed") + 1582 " (extra bits = " + paramBits + ")"); 1583 } 1584 inStream.skip(paramBits); 1585 bData.deferredDeliveryTimeRelativeSet = decodeSuccess; 1586 return decodeSuccess; 1587 } 1588 decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)1589 private static boolean decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream) 1590 throws BitwiseInputStream.AccessException { 1591 final int EXPECTED_PARAM_SIZE = 1 * 8; 1592 boolean decodeSuccess = false; 1593 int paramBits = inStream.read(8) * 8; 1594 if (paramBits >= EXPECTED_PARAM_SIZE) { 1595 paramBits -= EXPECTED_PARAM_SIZE; 1596 decodeSuccess = true; 1597 bData.validityPeriodRelative = inStream.read(8); 1598 } 1599 if ((! decodeSuccess) || (paramBits > 0)) { 1600 Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " + 1601 (decodeSuccess ? "succeeded" : "failed") + 1602 " (extra bits = " + paramBits + ")"); 1603 } 1604 inStream.skip(paramBits); 1605 bData.validityPeriodRelativeSet = decodeSuccess; 1606 return decodeSuccess; 1607 } 1608 decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)1609 private static boolean decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream) 1610 throws BitwiseInputStream.AccessException { 1611 final int EXPECTED_PARAM_SIZE = 1 * 8; 1612 boolean decodeSuccess = false; 1613 int paramBits = inStream.read(8) * 8; 1614 if (paramBits >= EXPECTED_PARAM_SIZE) { 1615 paramBits -= EXPECTED_PARAM_SIZE; 1616 decodeSuccess = true; 1617 bData.privacy = inStream.read(2); 1618 inStream.skip(6); 1619 } 1620 if ((! decodeSuccess) || (paramBits > 0)) { 1621 Rlog.d(LOG_TAG, "PRIVACY_INDICATOR decode " + 1622 (decodeSuccess ? "succeeded" : "failed") + 1623 " (extra bits = " + paramBits + ")"); 1624 } 1625 inStream.skip(paramBits); 1626 bData.privacyIndicatorSet = decodeSuccess; 1627 return decodeSuccess; 1628 } 1629 decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)1630 private static boolean decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream) 1631 throws BitwiseInputStream.AccessException { 1632 final int EXPECTED_PARAM_SIZE = 1 * 8; 1633 boolean decodeSuccess = false; 1634 int paramBits = inStream.read(8) * 8; 1635 if (paramBits >= EXPECTED_PARAM_SIZE) { 1636 paramBits -= EXPECTED_PARAM_SIZE; 1637 decodeSuccess = true; 1638 bData.language = inStream.read(8); 1639 } 1640 if ((! decodeSuccess) || (paramBits > 0)) { 1641 Rlog.d(LOG_TAG, "LANGUAGE_INDICATOR decode " + 1642 (decodeSuccess ? "succeeded" : "failed") + 1643 " (extra bits = " + paramBits + ")"); 1644 } 1645 inStream.skip(paramBits); 1646 bData.languageIndicatorSet = decodeSuccess; 1647 return decodeSuccess; 1648 } 1649 decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)1650 private static boolean decodeDisplayMode(BearerData bData, BitwiseInputStream inStream) 1651 throws BitwiseInputStream.AccessException { 1652 final int EXPECTED_PARAM_SIZE = 1 * 8; 1653 boolean decodeSuccess = false; 1654 int paramBits = inStream.read(8) * 8; 1655 if (paramBits >= EXPECTED_PARAM_SIZE) { 1656 paramBits -= EXPECTED_PARAM_SIZE; 1657 decodeSuccess = true; 1658 bData.displayMode = inStream.read(2); 1659 inStream.skip(6); 1660 } 1661 if ((! decodeSuccess) || (paramBits > 0)) { 1662 Rlog.d(LOG_TAG, "DISPLAY_MODE decode " + 1663 (decodeSuccess ? "succeeded" : "failed") + 1664 " (extra bits = " + paramBits + ")"); 1665 } 1666 inStream.skip(paramBits); 1667 bData.displayModeSet = decodeSuccess; 1668 return decodeSuccess; 1669 } 1670 decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)1671 private static boolean decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream) 1672 throws BitwiseInputStream.AccessException { 1673 final int EXPECTED_PARAM_SIZE = 1 * 8; 1674 boolean decodeSuccess = false; 1675 int paramBits = inStream.read(8) * 8; 1676 if (paramBits >= EXPECTED_PARAM_SIZE) { 1677 paramBits -= EXPECTED_PARAM_SIZE; 1678 decodeSuccess = true; 1679 bData.priority = inStream.read(2); 1680 inStream.skip(6); 1681 } 1682 if ((! decodeSuccess) || (paramBits > 0)) { 1683 Rlog.d(LOG_TAG, "PRIORITY_INDICATOR decode " + 1684 (decodeSuccess ? "succeeded" : "failed") + 1685 " (extra bits = " + paramBits + ")"); 1686 } 1687 inStream.skip(paramBits); 1688 bData.priorityIndicatorSet = decodeSuccess; 1689 return decodeSuccess; 1690 } 1691 decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)1692 private static boolean decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream) 1693 throws BitwiseInputStream.AccessException { 1694 final int EXPECTED_PARAM_SIZE = 1 * 8; 1695 boolean decodeSuccess = false; 1696 int paramBits = inStream.read(8) * 8; 1697 if (paramBits >= EXPECTED_PARAM_SIZE) { 1698 paramBits -= EXPECTED_PARAM_SIZE; 1699 decodeSuccess = true; 1700 bData.alert = inStream.read(2); 1701 inStream.skip(6); 1702 } 1703 if ((! decodeSuccess) || (paramBits > 0)) { 1704 Rlog.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " + 1705 (decodeSuccess ? "succeeded" : "failed") + 1706 " (extra bits = " + paramBits + ")"); 1707 } 1708 inStream.skip(paramBits); 1709 bData.alertIndicatorSet = decodeSuccess; 1710 return decodeSuccess; 1711 } 1712 decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)1713 private static boolean decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream) 1714 throws BitwiseInputStream.AccessException { 1715 final int EXPECTED_PARAM_SIZE = 1 * 8; 1716 boolean decodeSuccess = false; 1717 int paramBits = inStream.read(8) * 8; 1718 if (paramBits >= EXPECTED_PARAM_SIZE) { 1719 paramBits -= EXPECTED_PARAM_SIZE; 1720 decodeSuccess = true; 1721 bData.userResponseCode = inStream.read(8); 1722 } 1723 if ((! decodeSuccess) || (paramBits > 0)) { 1724 Rlog.d(LOG_TAG, "USER_RESPONSE_CODE decode " + 1725 (decodeSuccess ? "succeeded" : "failed") + 1726 " (extra bits = " + paramBits + ")"); 1727 } 1728 inStream.skip(paramBits); 1729 bData.userResponseCodeSet = decodeSuccess; 1730 return decodeSuccess; 1731 } 1732 decodeServiceCategoryProgramData(BearerData bData, BitwiseInputStream inStream)1733 private static boolean decodeServiceCategoryProgramData(BearerData bData, 1734 BitwiseInputStream inStream) throws BitwiseInputStream.AccessException, CodingException 1735 { 1736 if (inStream.available() < 13) { 1737 throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only " 1738 + inStream.available() + " bits available"); 1739 } 1740 1741 int paramBits = inStream.read(8) * 8; 1742 int msgEncoding = inStream.read(5); 1743 paramBits -= 5; 1744 1745 if (inStream.available() < paramBits) { 1746 throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only " 1747 + inStream.available() + " bits available (" + paramBits + " bits expected)"); 1748 } 1749 1750 ArrayList<CdmaSmsCbProgramData> programDataList = new ArrayList<CdmaSmsCbProgramData>(); 1751 1752 final int CATEGORY_FIELD_MIN_SIZE = 6 * 8; 1753 boolean decodeSuccess = false; 1754 while (paramBits >= CATEGORY_FIELD_MIN_SIZE) { 1755 int operation = inStream.read(4); 1756 int category = (inStream.read(8) << 8) | inStream.read(8); 1757 int language = inStream.read(8); 1758 int maxMessages = inStream.read(8); 1759 int alertOption = inStream.read(4); 1760 int numFields = inStream.read(8); 1761 paramBits -= CATEGORY_FIELD_MIN_SIZE; 1762 1763 int textBits = getBitsForNumFields(msgEncoding, numFields); 1764 if (paramBits < textBits) { 1765 throw new CodingException("category name is " + textBits + " bits in length," 1766 + " but there are only " + paramBits + " bits available"); 1767 } 1768 1769 UserData userData = new UserData(); 1770 userData.msgEncoding = msgEncoding; 1771 userData.msgEncodingSet = true; 1772 userData.numFields = numFields; 1773 userData.payload = inStream.readByteArray(textBits); 1774 paramBits -= textBits; 1775 1776 decodeUserDataPayload(userData, false); 1777 String categoryName = userData.payloadStr; 1778 CdmaSmsCbProgramData programData = new CdmaSmsCbProgramData(operation, category, 1779 language, maxMessages, alertOption, categoryName); 1780 programDataList.add(programData); 1781 1782 decodeSuccess = true; 1783 } 1784 1785 if ((! decodeSuccess) || (paramBits > 0)) { 1786 Rlog.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " + 1787 (decodeSuccess ? "succeeded" : "failed") + 1788 " (extra bits = " + paramBits + ')'); 1789 } 1790 1791 inStream.skip(paramBits); 1792 bData.serviceCategoryProgramData = programDataList; 1793 return decodeSuccess; 1794 } 1795 serviceCategoryToCmasMessageClass(int serviceCategory)1796 private static int serviceCategoryToCmasMessageClass(int serviceCategory) { 1797 switch (serviceCategory) { 1798 case SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT: 1799 return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT; 1800 1801 case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT: 1802 return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; 1803 1804 case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT: 1805 return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; 1806 1807 case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY: 1808 return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY; 1809 1810 case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE: 1811 return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST; 1812 1813 default: 1814 return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN; 1815 } 1816 } 1817 1818 /** 1819 * Calculates the number of bits to read for the specified number of encoded characters. 1820 * @param msgEncoding the message encoding to use 1821 * @param numFields the number of characters to read. For Shift-JIS and Korean encodings, 1822 * this is the number of bytes to read. 1823 * @return the number of bits to read from the stream 1824 * @throws CodingException if the specified encoding is not supported 1825 */ getBitsForNumFields(int msgEncoding, int numFields)1826 private static int getBitsForNumFields(int msgEncoding, int numFields) 1827 throws CodingException { 1828 switch (msgEncoding) { 1829 case UserData.ENCODING_OCTET: 1830 case UserData.ENCODING_SHIFT_JIS: 1831 case UserData.ENCODING_KOREAN: 1832 case UserData.ENCODING_LATIN: 1833 case UserData.ENCODING_LATIN_HEBREW: 1834 return numFields * 8; 1835 1836 case UserData.ENCODING_IA5: 1837 case UserData.ENCODING_7BIT_ASCII: 1838 case UserData.ENCODING_GSM_7BIT_ALPHABET: 1839 return numFields * 7; 1840 1841 case UserData.ENCODING_UNICODE_16: 1842 return numFields * 16; 1843 1844 default: 1845 throw new CodingException("unsupported message encoding (" + msgEncoding + ')'); 1846 } 1847 } 1848 1849 /** 1850 * CMAS message decoding. 1851 * (See TIA-1149-0-1, CMAS over CDMA) 1852 * 1853 * @param serviceCategory is the service category from the SMS envelope 1854 */ decodeCmasUserData(BearerData bData, int serviceCategory)1855 private static void decodeCmasUserData(BearerData bData, int serviceCategory) 1856 throws BitwiseInputStream.AccessException, CodingException { 1857 BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload); 1858 if (inStream.available() < 8) { 1859 throw new CodingException("emergency CB with no CMAE_protocol_version"); 1860 } 1861 int protocolVersion = inStream.read(8); 1862 if (protocolVersion != 0) { 1863 throw new CodingException("unsupported CMAE_protocol_version " + protocolVersion); 1864 } 1865 1866 int messageClass = serviceCategoryToCmasMessageClass(serviceCategory); 1867 int category = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN; 1868 int responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN; 1869 int severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; 1870 int urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; 1871 int certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; 1872 1873 while (inStream.available() >= 16) { 1874 int recordType = inStream.read(8); 1875 int recordLen = inStream.read(8); 1876 switch (recordType) { 1877 case 0: // Type 0 elements (Alert text) 1878 UserData alertUserData = new UserData(); 1879 alertUserData.msgEncoding = inStream.read(5); 1880 alertUserData.msgEncodingSet = true; 1881 alertUserData.msgType = 0; 1882 1883 int numFields; // number of chars to decode 1884 switch (alertUserData.msgEncoding) { 1885 case UserData.ENCODING_OCTET: 1886 case UserData.ENCODING_LATIN: 1887 numFields = recordLen - 1; // subtract 1 byte for encoding 1888 break; 1889 1890 case UserData.ENCODING_IA5: 1891 case UserData.ENCODING_7BIT_ASCII: 1892 case UserData.ENCODING_GSM_7BIT_ALPHABET: 1893 numFields = ((recordLen * 8) - 5) / 7; // subtract 5 bits for encoding 1894 break; 1895 1896 case UserData.ENCODING_UNICODE_16: 1897 numFields = (recordLen - 1) / 2; 1898 break; 1899 1900 default: 1901 numFields = 0; // unsupported encoding 1902 } 1903 1904 alertUserData.numFields = numFields; 1905 alertUserData.payload = inStream.readByteArray(recordLen * 8 - 5); 1906 decodeUserDataPayload(alertUserData, false); 1907 bData.userData = alertUserData; 1908 break; 1909 1910 case 1: // Type 1 elements 1911 category = inStream.read(8); 1912 responseType = inStream.read(8); 1913 severity = inStream.read(4); 1914 urgency = inStream.read(4); 1915 certainty = inStream.read(4); 1916 inStream.skip(recordLen * 8 - 28); 1917 break; 1918 1919 default: 1920 Rlog.w(LOG_TAG, "skipping unsupported CMAS record type " + recordType); 1921 inStream.skip(recordLen * 8); 1922 break; 1923 } 1924 } 1925 1926 bData.cmasWarningInfo = new SmsCbCmasInfo(messageClass, category, responseType, severity, 1927 urgency, certainty); 1928 } 1929 1930 /** 1931 * Create BearerData object from serialized representation. 1932 * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details) 1933 * 1934 * @param smsData byte array of raw encoded SMS bearer data. 1935 * @return an instance of BearerData. 1936 */ decode(byte[] smsData)1937 public static BearerData decode(byte[] smsData) { 1938 return decode(smsData, 0); 1939 } 1940 isCmasAlertCategory(int category)1941 private static boolean isCmasAlertCategory(int category) { 1942 return category >= SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT 1943 && category <= SmsEnvelope.SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE; 1944 } 1945 1946 /** 1947 * Create BearerData object from serialized representation. 1948 * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details) 1949 * 1950 * @param smsData byte array of raw encoded SMS bearer data. 1951 * @param serviceCategory the envelope service category (for CMAS alert handling) 1952 * @return an instance of BearerData. 1953 */ decode(byte[] smsData, int serviceCategory)1954 public static BearerData decode(byte[] smsData, int serviceCategory) { 1955 try { 1956 BitwiseInputStream inStream = new BitwiseInputStream(smsData); 1957 BearerData bData = new BearerData(); 1958 int foundSubparamMask = 0; 1959 while (inStream.available() > 0) { 1960 int subparamId = inStream.read(8); 1961 int subparamIdBit = 1 << subparamId; 1962 // int is 4 bytes. This duplicate check has a limit to Id number up to 32 (4*8) 1963 // as 32th bit is the max bit in int. 1964 // Per 3GPP2 C.S0015-B Table 4.5-1 Bearer Data Subparameter Identifiers: 1965 // last defined subparam ID is 23 (00010111 = 0x17 = 23). 1966 // Only do duplicate subparam ID check if subparam is within defined value as 1967 // reserved subparams are just skipped. 1968 if ((foundSubparamMask & subparamIdBit) != 0 && 1969 (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER && 1970 subparamId <= SUBPARAM_ID_LAST_DEFINED)) { 1971 throw new CodingException("illegal duplicate subparameter (" + 1972 subparamId + ")"); 1973 } 1974 boolean decodeSuccess; 1975 switch (subparamId) { 1976 case SUBPARAM_MESSAGE_IDENTIFIER: 1977 decodeSuccess = decodeMessageId(bData, inStream); 1978 break; 1979 case SUBPARAM_USER_DATA: 1980 decodeSuccess = decodeUserData(bData, inStream); 1981 break; 1982 case SUBPARAM_USER_RESPONSE_CODE: 1983 decodeSuccess = decodeUserResponseCode(bData, inStream); 1984 break; 1985 case SUBPARAM_REPLY_OPTION: 1986 decodeSuccess = decodeReplyOption(bData, inStream); 1987 break; 1988 case SUBPARAM_NUMBER_OF_MESSAGES: 1989 decodeSuccess = decodeMsgCount(bData, inStream); 1990 break; 1991 case SUBPARAM_CALLBACK_NUMBER: 1992 decodeSuccess = decodeCallbackNumber(bData, inStream); 1993 break; 1994 case SUBPARAM_MESSAGE_STATUS: 1995 decodeSuccess = decodeMsgStatus(bData, inStream); 1996 break; 1997 case SUBPARAM_MESSAGE_CENTER_TIME_STAMP: 1998 decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream); 1999 break; 2000 case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE: 2001 decodeSuccess = decodeValidityAbs(bData, inStream); 2002 break; 2003 case SUBPARAM_VALIDITY_PERIOD_RELATIVE: 2004 decodeSuccess = decodeValidityRel(bData, inStream); 2005 break; 2006 case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE: 2007 decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream); 2008 break; 2009 case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE: 2010 decodeSuccess = decodeDeferredDeliveryRel(bData, inStream); 2011 break; 2012 case SUBPARAM_PRIVACY_INDICATOR: 2013 decodeSuccess = decodePrivacyIndicator(bData, inStream); 2014 break; 2015 case SUBPARAM_LANGUAGE_INDICATOR: 2016 decodeSuccess = decodeLanguageIndicator(bData, inStream); 2017 break; 2018 case SUBPARAM_MESSAGE_DISPLAY_MODE: 2019 decodeSuccess = decodeDisplayMode(bData, inStream); 2020 break; 2021 case SUBPARAM_PRIORITY_INDICATOR: 2022 decodeSuccess = decodePriorityIndicator(bData, inStream); 2023 break; 2024 case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY: 2025 decodeSuccess = decodeMsgDeliveryAlert(bData, inStream); 2026 break; 2027 case SUBPARAM_MESSAGE_DEPOSIT_INDEX: 2028 decodeSuccess = decodeDepositIndex(bData, inStream); 2029 break; 2030 case SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA: 2031 decodeSuccess = decodeServiceCategoryProgramData(bData, inStream); 2032 break; 2033 default: 2034 decodeSuccess = decodeReserved(bData, inStream, subparamId); 2035 } 2036 if (decodeSuccess && 2037 (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER && 2038 subparamId <= SUBPARAM_ID_LAST_DEFINED)) { 2039 foundSubparamMask |= subparamIdBit; 2040 } 2041 } 2042 if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) { 2043 throw new CodingException("missing MESSAGE_IDENTIFIER subparam"); 2044 } 2045 if (bData.userData != null) { 2046 if (isCmasAlertCategory(serviceCategory)) { 2047 decodeCmasUserData(bData, serviceCategory); 2048 } else if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) { 2049 if ((foundSubparamMask ^ 2050 (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^ 2051 (1 << SUBPARAM_USER_DATA)) 2052 != 0) { 2053 Rlog.e(LOG_TAG, "IS-91 must occur without extra subparams (" + 2054 foundSubparamMask + ")"); 2055 } 2056 decodeIs91(bData); 2057 } else { 2058 decodeUserDataPayload(bData.userData, bData.hasUserDataHeader); 2059 } 2060 } 2061 return bData; 2062 } catch (BitwiseInputStream.AccessException ex) { 2063 Rlog.e(LOG_TAG, "BearerData decode failed: " + ex); 2064 } catch (CodingException ex) { 2065 Rlog.e(LOG_TAG, "BearerData decode failed: " + ex); 2066 } 2067 return null; 2068 } 2069 } 2070