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