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