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