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