1 /*
2  * Copyright (C) 2012 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.cellbroadcastservice.tests;
18 
19 import android.content.Context;
20 import android.hardware.radio.V1_0.CdmaSmsMessage;
21 import android.telephony.SmsCbCmasInfo;
22 import android.telephony.SmsCbMessage;
23 import android.telephony.cdma.CdmaSmsCbProgramData;
24 import android.testing.AndroidTestingRunner;
25 import android.testing.TestableLooper;
26 import android.util.Log;
27 
28 import com.android.cellbroadcastservice.BearerData;
29 import com.android.cellbroadcastservice.DefaultCellBroadcastService;
30 import com.android.internal.telephony.GsmAlphabet;
31 import com.android.internal.telephony.cdma.SmsMessage;
32 import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
33 import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress;
34 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
35 import com.android.internal.util.BitwiseOutputStream;
36 
37 import org.junit.After;
38 import org.junit.Before;
39 import org.junit.Ignore;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 
43 import java.util.Arrays;
44 import java.util.List;
45 import java.util.Random;
46 
47 /**
48  * Test cases to verify that our parseCdmaBroadcastSms function correctly works with the
49  * CdmaSmsMessage class.
50  */
51 @RunWith(AndroidTestingRunner.class)
52 @TestableLooper.RunWithLooper
53 public class CdmaSmsMessageTest extends CellBroadcastServiceTestBase {
54 
55     private static final String TAG = "CdmaSmsMessageTest";
56 
57     /* Copy of private subparameter identifier constants from BearerData class. */
58     private static final byte SUBPARAM_MESSAGE_IDENTIFIER = (byte) 0x00;
59     private static final byte SUBPARAM_USER_DATA = (byte) 0x01;
60     private static final byte SUBPARAM_PRIORITY_INDICATOR = (byte) 0x08;
61     private static final byte SUBPARAM_LANGUAGE_INDICATOR = (byte) 0x0D;
62     private static final byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA = 0x12;
63 
64     private static final int TELESERVICE_NOT_SET = 0x0000;
65     private static final int TELESERVICE_SCPT = 0x1006;
66 
67     /**
68      * Digit Mode Indicator is a 1-bit value that indicates whether
69      * the address digits are 4-bit DTMF codes or 8-bit codes.  (See
70      * 3GPP2 C.S0015-B, v2, 3.4.3.3)
71      */
72     private static final int DIGIT_MODE_4BIT_DTMF = 0x00;
73     private static final int DIGIT_MODE_8BIT_CHAR = 0x01;
74 
75     /**
76      * Number Mode Indicator is 1-bit value that indicates whether the
77      * address type is a data network address or not.  (See 3GPP2
78      * C.S0015-B, v2, 3.4.3.3)
79      */
80     private static final int NUMBER_MODE_NOT_DATA_NETWORK = 0x00;
81     private static final int NUMBER_MODE_DATA_NETWORK = 0x01;
82 
83     /**
84      * Number Types for data networks.
85      * (See 3GPP2 C.S005-D, table2.7.1.3.2.4-2 for complete table)
86      * (See 3GPP2 C.S0015-B, v2, 3.4.3.3 for data network subset)
87      * NOTE: value is stored in the parent class ton field.
88      */
89     private static final int TON_UNKNOWN = 0x00;
90 
91     /**
92      * Numbering Plan identification is a 0 or 4-bit value that
93      * indicates which numbering plan identification is set.  (See
94      * 3GPP2, C.S0015-B, v2, 3.4.3.3 and C.S005-D, table2.7.1.3.2.4-3)
95      */
96     private static final int NUMBERING_PLAN_ISDN_TELEPHONY = 0x1;
97 
98     /**
99      * User data encoding types.
100      * (See 3GPP2 C.R1001-F, v1.0, table 9.1-1)
101      */
102     public static final int ENCODING_OCTET = 0x00;
103     public static final int ENCODING_IS91_EXTENDED_PROTOCOL = 0x01;
104     public static final int ENCODING_7BIT_ASCII = 0x02;
105     public static final int ENCODING_IA5 = 0x03;
106     public static final int ENCODING_UNICODE_16 = 0x04;
107     public static final int ENCODING_SHIFT_JIS = 0x05;
108     public static final int ENCODING_KOREAN = 0x06;
109     public static final int ENCODING_LATIN_HEBREW = 0x07;
110     public static final int ENCODING_LATIN = 0x08;
111     public static final int ENCODING_GSM_7BIT_ALPHABET = 0x09;
112     public static final int ENCODING_GSM_DCS = 0x0A;
113 
114     /**
115      * IS-91 message types.
116      * (See TIA/EIS/IS-91-A-ENGL 1999, table 3.7.1.1-3)
117      */
118     public static final int IS91_MSG_TYPE_VOICEMAIL_STATUS = 0x82;
119     public static final int IS91_MSG_TYPE_SHORT_MESSAGE_FULL = 0x83;
120     public static final int IS91_MSG_TYPE_CLI = 0x84;
121     public static final int IS91_MSG_TYPE_SHORT_MESSAGE = 0x85;
122 
123     /**
124      * Supported message types for CDMA SMS messages
125      * (See 3GPP2 C.S0015-B, v2.0, table 4.5.1-1)
126      */
127     public static final int MESSAGE_TYPE_DELIVER = 0x01;
128     public static final int MESSAGE_TYPE_SUBMIT = 0x02;
129     public static final int MESSAGE_TYPE_CANCELLATION = 0x03;
130     public static final int MESSAGE_TYPE_DELIVERY_ACK = 0x04;
131 
132     @Before
setUp()133     public void setUp() throws Exception {
134         super.setUp();
135         putResources(com.android.cellbroadcastservice.R.bool.config_sms_utf8_support, false);
136     }
137 
138     @After
tearDown()139     public void tearDown() throws Exception {
140         super.tearDown();
141     }
142 
143     /**
144      * Initialize a Parcel for an incoming CDMA cell broadcast. The caller will write the
145      * bearer data and then convert it to an SmsMessage.
146      *
147      * @param serviceCategory the CDMA service category
148      * @return the initialized Parcel
149      */
createBroadcastParcel(int serviceCategory)150     private static CdmaSmsMessage createBroadcastParcel(int serviceCategory) {
151         CdmaSmsMessage msg = new CdmaSmsMessage();
152 
153         msg.teleserviceId = TELESERVICE_NOT_SET;
154         msg.isServicePresent = true;
155         msg.serviceCategory = serviceCategory;
156 
157         // dummy address (RIL may generate a different dummy address for broadcasts)
158         msg.address.digitMode = DIGIT_MODE_4BIT_DTMF;
159         msg.address.numberMode = NUMBER_MODE_NOT_DATA_NETWORK;
160         msg.address.numberType = TON_UNKNOWN;
161         msg.address.numberPlan = NUMBERING_PLAN_ISDN_TELEPHONY;
162         msg.subAddress.subaddressType = 0;
163         msg.subAddress.odd = false;
164         return msg;
165     }
166 
167     /**
168      * Initialize a BitwiseOutputStream with the CDMA bearer data subparameters except for
169      * user data. The caller will append the user data and add it to the parcel.
170      *
171      * @param messageId the 16-bit message identifier
172      * @param priority  message priority
173      * @param language  message language code
174      * @return the initialized BitwiseOutputStream
175      */
createBearerDataStream(int messageId, int priority, int language)176     private static BitwiseOutputStream createBearerDataStream(int messageId, int priority,
177             int language) throws BitwiseOutputStream.AccessException {
178         BitwiseOutputStream bos = new BitwiseOutputStream(10);
179         bos.write(8, SUBPARAM_MESSAGE_IDENTIFIER);
180         bos.write(8, 3);    // length: 3 bytes
181         bos.write(4, BearerData.MESSAGE_TYPE_DELIVER);
182         bos.write(8, ((messageId >>> 8) & 0xff));
183         bos.write(8, (messageId & 0xff));
184         bos.write(1, 0);    // no User Data Header
185         bos.write(3, 0);    // reserved
186 
187         if (priority != -1) {
188             bos.write(8, SUBPARAM_PRIORITY_INDICATOR);
189             bos.write(8, 1);    // length: 1 byte
190             bos.write(2, (priority & 0x03));
191             bos.write(6, 0);    // reserved
192         }
193 
194         if (language != -1) {
195             bos.write(8, SUBPARAM_LANGUAGE_INDICATOR);
196             bos.write(8, 1);    // length: 1 byte
197             bos.write(8, (language & 0xff));
198         }
199 
200         return bos;
201     }
202 
203     /**
204      * Convert CdmaSmsMessage defined in radio/1.0/types.hal to SmsMessage
205      * Note only primitive fields are set
206      * @param cdmaSmsMessage CdmaSmsMessage defined in radio/1.0/types.hal
207      * @return A converted SmsMessage
208      * TODO: remove this method and use the one in RILUtils
209      */
convertHalCdmaSmsMessage( android.hardware.radio.V1_0.CdmaSmsMessage cdmaSmsMessage)210     private static SmsMessage convertHalCdmaSmsMessage(
211             android.hardware.radio.V1_0.CdmaSmsMessage cdmaSmsMessage) {
212         // Note: Parcel.readByte actually reads one Int and masks to byte
213         SmsEnvelope env = new SmsEnvelope();
214         CdmaSmsAddress addr = new CdmaSmsAddress();
215         CdmaSmsSubaddress subaddr = new CdmaSmsSubaddress();
216         byte[] data;
217         byte count;
218         int countInt;
219         int addressDigitMode;
220 
221         //currently not supported by the modem-lib: env.mMessageType
222         env.teleService = cdmaSmsMessage.teleserviceId;
223 
224         if (cdmaSmsMessage.isServicePresent) {
225             env.messageType = SmsEnvelope.MESSAGE_TYPE_BROADCAST;
226         } else {
227             if (SmsEnvelope.TELESERVICE_NOT_SET == env.teleService) {
228                 // assume type ACK
229                 env.messageType = SmsEnvelope.MESSAGE_TYPE_ACKNOWLEDGE;
230             } else {
231                 env.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
232             }
233         }
234         env.serviceCategory = cdmaSmsMessage.serviceCategory;
235 
236         // address
237         addressDigitMode = cdmaSmsMessage.address.digitMode;
238         addr.digitMode = (byte) (0xFF & addressDigitMode);
239         addr.numberMode = (byte) (0xFF & cdmaSmsMessage.address.numberMode);
240         addr.ton = cdmaSmsMessage.address.numberType;
241         addr.numberPlan = (byte) (0xFF & cdmaSmsMessage.address.numberPlan);
242         count = (byte) cdmaSmsMessage.address.digits.size();
243         addr.numberOfDigits = count;
244         data = new byte[count];
245         for (int index = 0; index < count; index++) {
246             data[index] = cdmaSmsMessage.address.digits.get(index);
247 
248             // convert the value if it is 4-bit DTMF to 8 bit
249             if (addressDigitMode == CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF) {
250                 data[index] = SmsMessage.convertDtmfToAscii(data[index]);
251             }
252         }
253 
254         addr.origBytes = data;
255 
256         subaddr.type = cdmaSmsMessage.subAddress.subaddressType;
257         subaddr.odd = (byte) (cdmaSmsMessage.subAddress.odd ? 1 : 0);
258         count = (byte) cdmaSmsMessage.subAddress.digits.size();
259 
260         if (count < 0) {
261             count = 0;
262         }
263 
264         // p_cur->sSubAddress.digits[digitCount] :
265 
266         data = new byte[count];
267 
268         for (int index = 0; index < count; ++index) {
269             data[index] = cdmaSmsMessage.subAddress.digits.get(index);
270         }
271 
272         subaddr.origBytes = data;
273 
274         /* currently not supported by the modem-lib:
275             env.bearerReply
276             env.replySeqNo
277             env.errorClass
278             env.causeCode
279         */
280 
281         // bearer data
282         countInt = cdmaSmsMessage.bearerData.size();
283         if (countInt < 0) {
284             countInt = 0;
285         }
286 
287         data = new byte[countInt];
288         for (int index = 0; index < countInt; index++) {
289             data[index] = cdmaSmsMessage.bearerData.get(index);
290         }
291         // BD gets further decoded when accessed in SMSDispatcher
292         env.bearerData = data;
293 
294         // link the filled objects to the SMS
295         env.origAddress = addr;
296         env.origSubaddress = subaddr;
297 
298         SmsMessage msg = new SmsMessage(addr, env);
299 
300         return msg;
301     }
302 
303     /**
304      * Write the bearer data array to the parcel, then return a new SmsMessage from the parcel.
305      *
306      * @param msg        CdmaSmsMessage containing the CDMA SMS headers
307      * @param bearerData the bearer data byte array to append to the parcel
308      * @return the new SmsMessage created from the parcel
309      */
createMessageFromParcel(CdmaSmsMessage msg, byte[] bearerData)310     private static SmsMessage createMessageFromParcel(CdmaSmsMessage msg, byte[] bearerData) {
311         for (byte b : bearerData) {
312             msg.bearerData.add(b);
313         }
314         SmsMessage message = convertHalCdmaSmsMessage(msg);
315         return message;
316     }
317 
318     /**
319      * Create a parcel for an incoming CMAS broadcast, then return a new SmsMessage created
320      * from the parcel.
321      *
322      * @param serviceCategory the CDMA service category
323      * @param messageId       the 16-bit message identifier
324      * @param priority        message priority
325      * @param language        message language code
326      * @param body            message body
327      * @param cmasCategory    CMAS category (or -1 to skip adding CMAS type 1 elements record)
328      * @param responseType    CMAS response type
329      * @param severity        CMAS severity
330      * @param urgency         CMAS urgency
331      * @param certainty       CMAS certainty
332      * @return the newly created SmsMessage object
333      */
createCmasSmsMessage(int serviceCategory, int messageId, int priority, int language, int encoding, String body, int cmasCategory, int responseType, int severity, int urgency, int certainty)334     private static SmsMessage createCmasSmsMessage(int serviceCategory, int messageId, int priority,
335             int language, int encoding, String body, int cmasCategory, int responseType,
336             int severity, int urgency, int certainty) throws Exception {
337         BitwiseOutputStream cmasBos = new BitwiseOutputStream(10);
338         cmasBos.write(8, 0);    // CMAE protocol version 0
339 
340         if (body != null) {
341             cmasBos.write(8, 0);        // Type 0 elements (alert text)
342             encodeBody(encoding, body, true, cmasBos);
343         }
344 
345         if (cmasCategory != SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN) {
346             cmasBos.write(8, 1);    // Type 1 elements
347             cmasBos.write(8, 4);    // length: 4 bytes
348             cmasBos.write(8, (cmasCategory & 0xff));
349             cmasBos.write(8, (responseType & 0xff));
350             cmasBos.write(4, (severity & 0x0f));
351             cmasBos.write(4, (urgency & 0x0f));
352             cmasBos.write(4, (certainty & 0x0f));
353             cmasBos.write(4, 0);    // pad to octet boundary
354         }
355 
356         byte[] cmasUserData = cmasBos.toByteArray();
357 
358         CdmaSmsMessage msg = createBroadcastParcel(serviceCategory);
359         BitwiseOutputStream bos = createBearerDataStream(messageId, priority, language);
360 
361         bos.write(8, SUBPARAM_USER_DATA);
362         bos.write(8, cmasUserData.length + 2);  // add 2 bytes for msg_encoding and num_fields
363         bos.write(5, ENCODING_OCTET);
364         bos.write(8, cmasUserData.length);
365         bos.writeByteArray(cmasUserData.length * 8, cmasUserData);
366         bos.write(3, 0);    // pad to byte boundary
367 
368         return createMessageFromParcel(msg, bos.toByteArray());
369     }
370 
371     /**
372      * Create a parcel for an incoming CDMA cell broadcast, then return a new SmsMessage created
373      * from the parcel.
374      *
375      * @param serviceCategory the CDMA service category
376      * @param messageId       the 16-bit message identifier
377      * @param priority        message priority
378      * @param language        message language code
379      * @param encoding        user data encoding method
380      * @param body            the message body
381      * @return the newly created SmsMessage object
382      */
createBroadcastSmsMessage(int serviceCategory, int messageId, int priority, int language, int encoding, String body)383     private static SmsMessage createBroadcastSmsMessage(int serviceCategory, int messageId,
384             int priority, int language, int encoding, String body) throws Exception {
385         CdmaSmsMessage msg = createBroadcastParcel(serviceCategory);
386         BitwiseOutputStream bos = createBearerDataStream(messageId, priority, language);
387 
388         bos.write(8, SUBPARAM_USER_DATA);
389         encodeBody(encoding, body, false, bos);
390 
391         return createMessageFromParcel(msg, bos.toByteArray());
392     }
393 
394     /**
395      * Append the message length, encoding, and body to the BearerData output stream.
396      * This is used for writing the User Data subparameter for non-CMAS broadcasts and for
397      * writing the alert text for CMAS broadcasts.
398      *
399      * @param encoding     one of the CDMA UserData encoding values
400      * @param body         the message body
401      * @param isCmasRecord true if this is a CMAS type 0 elements record; false for user data
402      * @param bos          the BitwiseOutputStream to write to
403      * @throws Exception on any encoding error
404      */
encodeBody(int encoding, String body, boolean isCmasRecord, BitwiseOutputStream bos)405     private static void encodeBody(int encoding, String body, boolean isCmasRecord,
406             BitwiseOutputStream bos) throws Exception {
407         if (encoding == ENCODING_7BIT_ASCII || encoding == ENCODING_IA5) {
408             int charCount = body.length();
409             int recordBits = (charCount * 7) + 5;       // add 5 bits for char set field
410             int recordOctets = (recordBits + 7) / 8;    // round up to octet boundary
411             int padBits = (recordOctets * 8) - recordBits;
412 
413             if (!isCmasRecord) {
414                 recordOctets++;                         // add 8 bits for num_fields
415             }
416 
417             bos.write(8, recordOctets);
418             bos.write(5, (encoding & 0x1f));
419 
420             if (!isCmasRecord) {
421                 bos.write(8, charCount);
422             }
423 
424             for (int i = 0; i < charCount; i++) {
425                 bos.write(7, body.charAt(i));
426             }
427 
428             bos.write(padBits, 0);      // pad to octet boundary
429         } else if (encoding == ENCODING_GSM_7BIT_ALPHABET
430                 || encoding == ENCODING_GSM_DCS) {
431             // convert to 7-bit packed encoding with septet count in index 0 of byte array
432             byte[] encodedBody = GsmAlphabet.stringToGsm7BitPacked(body);
433 
434             int charCount = encodedBody[0];             // septet count
435             int recordBits = (charCount * 7) + 5;       // add 5 bits for char set field
436             int recordOctets = (recordBits + 7) / 8;    // round up to octet boundary
437             int padBits = (recordOctets * 8) - recordBits;
438 
439             if (!isCmasRecord) {
440                 recordOctets++;                         // add 8 bits for num_fields
441                 if (encoding == ENCODING_GSM_DCS) {
442                     recordOctets++;                     // add 8 bits for DCS (message type)
443                 }
444             }
445 
446             bos.write(8, recordOctets);
447             bos.write(5, (encoding & 0x1f));
448 
449             if (!isCmasRecord && encoding == ENCODING_GSM_DCS) {
450                 bos.write(8, 0);        // GSM DCS: 7 bit default alphabet, no msg class
451             }
452 
453             if (!isCmasRecord) {
454                 bos.write(8, charCount);
455             }
456             byte[] bodySeptets = Arrays.copyOfRange(encodedBody, 1, encodedBody.length);
457             bos.writeByteArray(charCount * 7, bodySeptets);
458             bos.write(padBits, 0);      // pad to octet boundary
459         } else if (encoding == ENCODING_IS91_EXTENDED_PROTOCOL) {
460             // 6 bit packed encoding with 0x20 offset (ASCII 0x20 - 0x60)
461             int charCount = body.length();
462             int recordBits = (charCount * 6) + 21;      // add 21 bits for header fields
463             int recordOctets = (recordBits + 7) / 8;    // round up to octet boundary
464             int padBits = (recordOctets * 8) - recordBits;
465 
466             bos.write(8, recordOctets);
467 
468             bos.write(5, (encoding & 0x1f));
469             bos.write(8, IS91_MSG_TYPE_SHORT_MESSAGE);
470             bos.write(8, charCount);
471 
472             for (int i = 0; i < charCount; i++) {
473                 bos.write(6, ((int) body.charAt(i) - 0x20));
474             }
475 
476             bos.write(padBits, 0);      // pad to octet boundary
477         } else {
478             byte[] encodedBody;
479             switch (encoding) {
480                 case ENCODING_UNICODE_16:
481                     encodedBody = body.getBytes("UTF-16BE");
482                     break;
483 
484                 case ENCODING_SHIFT_JIS:
485                     encodedBody = body.getBytes("Shift_JIS");
486                     break;
487 
488                 case ENCODING_KOREAN:
489                     encodedBody = body.getBytes("KSC5601");
490                     break;
491 
492                 case ENCODING_LATIN_HEBREW:
493                     encodedBody = body.getBytes("ISO-8859-8");
494                     break;
495 
496                 case ENCODING_LATIN:
497                 default:
498                     encodedBody = body.getBytes("ISO-8859-1");
499                     break;
500             }
501             int charCount = body.length();              // use actual char count for num fields
502             int recordOctets = encodedBody.length + 1;  // add 1 byte for encoding and pad bits
503             if (!isCmasRecord) {
504                 recordOctets++;                         // add 8 bits for num_fields
505             }
506             bos.write(8, recordOctets);
507             bos.write(5, (encoding & 0x1f));
508             if (!isCmasRecord) {
509                 bos.write(8, charCount);
510             }
511             bos.writeByteArray(encodedBody.length * 8, encodedBody);
512             bos.write(3, 0);            // pad to octet boundary
513         }
514     }
515 
516     private static final String TEST_TEXT = "This is a test CDMA cell broadcast message..."
517             + "678901234567890123456789012345678901234567890";
518 
519     private static final String PRES_ALERT =
520             "THE PRESIDENT HAS ISSUED AN EMERGENCY ALERT. CHECK LOCAL MEDIA FOR MORE DETAILS";
521 
522     private static final String EXTREME_ALERT = "FLASH FLOOD WARNING FOR SOUTH COCONINO COUNTY"
523             + " - NORTH CENTRAL ARIZONA UNTIL 415 PM MST";
524 
525     private static final String SEVERE_ALERT = "SEVERE WEATHER WARNING FOR SOMERSET COUNTY"
526             + " - NEW JERSEY UNTIL 415 PM MST";
527 
528     private static final String AMBER_ALERT =
529             "AMBER ALERT:Mountain View,CA VEH'07 Blue Honda Civic CA LIC 5ABC123";
530 
531     private static final String MONTHLY_TEST_ALERT = "This is a test of the emergency alert system."
532             + " This is only a test. 89012345678901234567890";
533 
534     private static final String IS91_TEXT = "IS91 SHORT MSG";   // max length 14 chars
535 
536     /**
537      * Verify that the SmsCbMessage has the correct values for CDMA.
538      *
539      * @param cbMessage the message to test
540      */
verifyCbValues(SmsCbMessage cbMessage)541     private static void verifyCbValues(SmsCbMessage cbMessage) {
542         assertEquals(SmsCbMessage.MESSAGE_FORMAT_3GPP2, cbMessage.getMessageFormat());
543         assertEquals(SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE, cbMessage.getGeographicalScope());
544         assertEquals(false, cbMessage.isEtwsMessage()); // ETWS on CDMA not currently supported
545     }
546 
doTestNonEmergencyBroadcast(Context context, int encoding)547     private static void doTestNonEmergencyBroadcast(Context context, int encoding)
548             throws Exception {
549         SmsMessage msg = createBroadcastSmsMessage(123, 456, BearerData.PRIORITY_NORMAL,
550                 BearerData.LANGUAGE_ENGLISH, encoding, TEST_TEXT);
551 
552         SmsCbMessage cbMessage =
553                 DefaultCellBroadcastService.parseCdmaBroadcastSms(context,
554                         0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
555         //SmsCbMessage cbMessage = msg.parseCdmaBroadcastSms("", 0);
556         verifyCbValues(cbMessage);
557         assertEquals(123, cbMessage.getServiceCategory());
558         assertEquals(456, cbMessage.getSerialNumber());
559         assertEquals(SmsCbMessage.MESSAGE_PRIORITY_NORMAL, cbMessage.getMessagePriority());
560         assertEquals("en", cbMessage.getLanguageCode());
561         assertEquals(TEST_TEXT, cbMessage.getMessageBody());
562         assertEquals(false, cbMessage.isEmergencyMessage());
563         assertEquals(false, cbMessage.isCmasMessage());
564     }
565 
566     @Test
testNonEmergencyBroadcast7bitAscii()567     public void testNonEmergencyBroadcast7bitAscii() throws Exception {
568         doTestNonEmergencyBroadcast(mMockedContext, ENCODING_7BIT_ASCII);
569     }
570 
571     @Test
testNonEmergencyBroadcast7bitGsm()572     public void testNonEmergencyBroadcast7bitGsm() throws Exception {
573         doTestNonEmergencyBroadcast(mMockedContext, ENCODING_GSM_7BIT_ALPHABET);
574     }
575 
576     @Test
testNonEmergencyBroadcast16bitUnicode()577     public void testNonEmergencyBroadcast16bitUnicode() throws Exception {
578         doTestNonEmergencyBroadcast(mMockedContext, ENCODING_UNICODE_16);
579     }
580 
doTestCmasBroadcast(Context context, int serviceCategory, int messageClass, String body)581     private static void doTestCmasBroadcast(Context context, int serviceCategory, int messageClass,
582             String body) throws Exception {
583         SmsMessage msg = createCmasSmsMessage(
584                 serviceCategory, 1234, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
585                 ENCODING_7BIT_ASCII, body, -1, -1, -1, -1, -1);
586 
587         SmsCbMessage cbMessage =
588                 DefaultCellBroadcastService.parseCdmaBroadcastSms(context,
589                         0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
590         //SmsCbMessage cbMessage = msg.parseCdmaBroadcastSms("", 0);
591         verifyCbValues(cbMessage);
592         assertEquals(serviceCategory, cbMessage.getServiceCategory());
593         assertEquals(1234, cbMessage.getSerialNumber());
594         assertEquals(SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, cbMessage.getMessagePriority());
595         assertEquals("en", cbMessage.getLanguageCode());
596         assertEquals(body, cbMessage.getMessageBody());
597         assertEquals(true, cbMessage.isEmergencyMessage());
598         assertEquals(true, cbMessage.isCmasMessage());
599         SmsCbCmasInfo cmasInfo = cbMessage.getCmasWarningInfo();
600         assertEquals(messageClass, cmasInfo.getMessageClass());
601         assertEquals(SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN, cmasInfo.getCategory());
602         assertEquals(SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, cmasInfo.getResponseType());
603         assertEquals(SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN, cmasInfo.getSeverity());
604         assertEquals(SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN, cmasInfo.getUrgency());
605         assertEquals(SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN, cmasInfo.getCertainty());
606     }
607 
608     @Test
testCmasPresidentialAlert()609     public void testCmasPresidentialAlert() throws Exception {
610         doTestCmasBroadcast(mMockedContext,
611                 CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
612                 SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT, PRES_ALERT);
613     }
614 
615     @Test
testCmasExtremeAlert()616     public void testCmasExtremeAlert() throws Exception {
617         doTestCmasBroadcast(mMockedContext, CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
618                 SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT, EXTREME_ALERT);
619     }
620 
621     @Test
testCmasSevereAlert()622     public void testCmasSevereAlert() throws Exception {
623         doTestCmasBroadcast(mMockedContext, CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT,
624                 SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT, SEVERE_ALERT);
625     }
626 
627     @Test
testCmasAmberAlert()628     public void testCmasAmberAlert() throws Exception {
629         doTestCmasBroadcast(mMockedContext,
630                 CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY,
631                 SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY, AMBER_ALERT);
632     }
633 
634     @Test
testCmasTestMessage()635     public void testCmasTestMessage() throws Exception {
636         doTestCmasBroadcast(mMockedContext, CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE,
637                 SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST, MONTHLY_TEST_ALERT);
638     }
639 
640     @Test
testCmasExtremeAlertType1Elements()641     public void testCmasExtremeAlertType1Elements() throws Exception {
642         SmsMessage msg = createCmasSmsMessage(CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
643                 5678, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
644                 ENCODING_7BIT_ASCII, EXTREME_ALERT, SmsCbCmasInfo.CMAS_CATEGORY_ENV,
645                 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR, SmsCbCmasInfo.CMAS_SEVERITY_SEVERE,
646                 SmsCbCmasInfo.CMAS_URGENCY_EXPECTED, SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY);
647 
648         SmsCbMessage cbMessage =
649                 DefaultCellBroadcastService.parseCdmaBroadcastSms(mMockedContext,
650                         0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
651         //SmsCbMessage cbMessage = msg.parseCdmaBroadcastSms("", 0);
652         verifyCbValues(cbMessage);
653         assertEquals(CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
654                 cbMessage.getServiceCategory());
655         assertEquals(5678, cbMessage.getSerialNumber());
656         assertEquals(SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, cbMessage.getMessagePriority());
657         assertEquals("en", cbMessage.getLanguageCode());
658         assertEquals(EXTREME_ALERT, cbMessage.getMessageBody());
659         assertEquals(true, cbMessage.isEmergencyMessage());
660         assertEquals(true, cbMessage.isCmasMessage());
661         SmsCbCmasInfo cmasInfo = cbMessage.getCmasWarningInfo();
662         assertEquals(SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT, cmasInfo.getMessageClass());
663         assertEquals(SmsCbCmasInfo.CMAS_CATEGORY_ENV, cmasInfo.getCategory());
664         assertEquals(SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR, cmasInfo.getResponseType());
665         assertEquals(SmsCbCmasInfo.CMAS_SEVERITY_SEVERE, cmasInfo.getSeverity());
666         assertEquals(SmsCbCmasInfo.CMAS_URGENCY_EXPECTED, cmasInfo.getUrgency());
667         assertEquals(SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY, cmasInfo.getCertainty());
668     }
669 
670     // VZW requirement is to discard message with unsupported charset. Verify that we return null
671     // for this unsupported character set.
672     @Ignore
673     @Test
testCmasUnsupportedCharSet()674     public void testCmasUnsupportedCharSet() throws Exception {
675         SmsMessage msg = createCmasSmsMessage(CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
676                 12345, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
677                 0x1F, EXTREME_ALERT, -1, -1, -1, -1, -1);
678 
679         SmsCbMessage cbMessage =
680                 DefaultCellBroadcastService.parseCdmaBroadcastSms(mMockedContext,
681                         0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
682         //SmsCbMessage cbMessage = msg.parseCdmaBroadcastSms("", 0);
683         assertNull("expected null for unsupported charset", cbMessage);
684     }
685 
686     // VZW requirement is to discard message with unsupported charset. Verify that we return null
687     // for this unsupported character set.
688     @Test
testCmasUnsupportedCharSet2()689     public void testCmasUnsupportedCharSet2() throws Exception {
690         SmsMessage msg = createCmasSmsMessage(CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
691                 67890, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
692                 ENCODING_KOREAN, EXTREME_ALERT, -1, -1, -1, -1, -1);
693 
694         SmsCbMessage cbMessage =
695                 DefaultCellBroadcastService.parseCdmaBroadcastSms(mMockedContext,
696                         0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
697         //SmsCbMessage cbMessage = msg.parseCdmaBroadcastSms("", 0);
698         assertNull("expected null for unsupported charset", cbMessage);
699     }
700 
701     // VZW requirement is to discard message without record type 0. The framework will decode it
702     // and the app will discard it.
703     @Test
testCmasNoRecordType0()704     public void testCmasNoRecordType0() throws Exception {
705         SmsMessage msg = createCmasSmsMessage(
706                 CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT, 1234,
707                 BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
708                 ENCODING_7BIT_ASCII, null, -1, -1, -1, -1, -1);
709 
710         SmsCbMessage cbMessage =
711                 DefaultCellBroadcastService.parseCdmaBroadcastSms(mMockedContext,
712                         0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
713         //SmsCbMessage cbMessage = msg.parseCdmaBroadcastSms("", 0);
714         verifyCbValues(cbMessage);
715         assertEquals(CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
716                 cbMessage.getServiceCategory());
717         assertEquals(1234, cbMessage.getSerialNumber());
718         assertEquals(SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, cbMessage.getMessagePriority());
719         assertEquals("en", cbMessage.getLanguageCode());
720         assertEquals(null, cbMessage.getMessageBody());
721         assertEquals(true, cbMessage.isEmergencyMessage());
722         assertEquals(true, cbMessage.isCmasMessage());
723         SmsCbCmasInfo cmasInfo = cbMessage.getCmasWarningInfo();
724         assertEquals(SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT, cmasInfo.getMessageClass());
725         assertEquals(SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN, cmasInfo.getCategory());
726         assertEquals(SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, cmasInfo.getResponseType());
727         assertEquals(SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN, cmasInfo.getSeverity());
728         assertEquals(SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN, cmasInfo.getUrgency());
729         assertEquals(SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN, cmasInfo.getCertainty());
730     }
731 
732     // Make sure we don't throw an exception if we feed completely random data to BearerStream.
733     @Test
testRandomBearerStreamData()734     public void testRandomBearerStreamData() {
735         Random r = new Random(54321);
736         for (int run = 0; run < 1000; run++) {
737             int len = r.nextInt(140);
738             byte[] data = new byte[len];
739             for (int i = 0; i < len; i++) {
740                 data[i] = (byte) r.nextInt(256);
741             }
742             // Log.d(TAG, "trying random bearer data run " + run + " length " + len);
743             try {
744                 int category = 0x0ff0 + r.nextInt(32);  // half CMAS, half non-CMAS
745                 CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(category);
746                 SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, data);
747                 SmsCbMessage cbMessage =
748                         DefaultCellBroadcastService.parseCdmaBroadcastSms(
749                                 mMockedContext,
750                                 0, "", msg.getEnvelopeBearerData(),
751                                 msg.getEnvelopeServiceCategory());
752                 //SmsCbMessage cbMessage = msg.parseCdmaBroadcastSms("", 0);
753                 // with random input, cbMessage will almost always be null (log when it isn't)
754                 if (cbMessage != null) {
755                     Log.d(TAG, "success: " + cbMessage);
756                 }
757             } catch (Exception e) {
758                 Log.d(TAG, "exception thrown", e);
759                 fail("Exception in decoder at run " + run + " length " + len + ": " + e);
760             }
761         }
762     }
763 
764     // Make sure we don't throw an exception if we put random data in the UserData subparam.
765     @Test
testRandomUserData()766     public void testRandomUserData() {
767         Random r = new Random(94040);
768         for (int run = 0; run < 1000; run++) {
769             int category = 0x0ff0 + r.nextInt(32);  // half CMAS, half non-CMAS
770             CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(category);
771             int len = r.nextInt(140);
772             // Log.d(TAG, "trying random user data run " + run + " length " + len);
773 
774             try {
775                 BitwiseOutputStream bos = createBearerDataStream(r.nextInt(65536), r.nextInt(4),
776                         r.nextInt(256));
777 
778                 bos.write(8, SUBPARAM_USER_DATA);
779                 bos.write(8, len);
780 
781                 for (int i = 0; i < len; i++) {
782                     bos.write(8, r.nextInt(256));
783                 }
784 
785                 SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
786                 SmsCbMessage cbMessage =
787                         DefaultCellBroadcastService.parseCdmaBroadcastSms(mMockedContext, 0, "",
788                                 msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
789                 //SmsCbMessage cbMessage = msg.parseCdmaBroadcastSms("", 0);
790             } catch (Exception e) {
791                 Log.d(TAG, "exception thrown", e);
792                 fail("Exception in decoder at run " + run + " length " + len + ": " + e);
793             }
794         }
795     }
796 
797     /**
798      * Initialize a Parcel for incoming Service Category Program Data teleservice. The caller will
799      * write the bearer data and then convert it to an SmsMessage.
800      *
801      * @return the initialized Parcel
802      */
createServiceCategoryProgramDataParcel()803     private static CdmaSmsMessage createServiceCategoryProgramDataParcel() {
804         CdmaSmsMessage msg = new CdmaSmsMessage();
805 
806         msg.teleserviceId = TELESERVICE_SCPT;
807         msg.isServicePresent = false;
808         msg.serviceCategory = 0;
809 
810         // dummy address (RIL may generate a different dummy address for broadcasts)
811         msg.address.digitMode = DIGIT_MODE_4BIT_DTMF;
812         msg.address.numberMode = NUMBER_MODE_NOT_DATA_NETWORK;
813         msg.address.numberType = TON_UNKNOWN;
814         msg.address.numberPlan = NUMBERING_PLAN_ISDN_TELEPHONY;
815         msg.subAddress.subaddressType = 0;
816         msg.subAddress.odd = false;
817         return msg;
818     }
819 
820     private static final String CAT_EXTREME_THREAT = "Extreme Threat to Life and Property";
821     private static final String CAT_SEVERE_THREAT = "Severe Threat to Life and Property";
822     private static final String CAT_AMBER_ALERTS = "AMBER Alerts";
823 
824     @Test
testServiceCategoryProgramDataAddCategory()825     public void testServiceCategoryProgramDataAddCategory() throws Exception {
826         CdmaSmsMessage cdmaSmsMessage = createServiceCategoryProgramDataParcel();
827         BitwiseOutputStream bos = createBearerDataStream(123, -1, -1);
828 
829         int categoryNameLength = CAT_EXTREME_THREAT.length();
830         int subparamLengthBits = (53 + (categoryNameLength * 7));
831         int subparamLengthBytes = (subparamLengthBits + 7) / 8;
832         int subparamPadBits = (subparamLengthBytes * 8) - subparamLengthBits;
833 
834         bos.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA);
835         bos.write(8, subparamLengthBytes);
836         bos.write(5, ENCODING_7BIT_ASCII);
837 
838         bos.write(4, CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY);
839         bos.write(8, (CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT >>> 8));
840         bos.write(8, (CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT & 0xff));
841         bos.write(8, BearerData.LANGUAGE_ENGLISH);
842         bos.write(8, 100);  // max messages
843         bos.write(4, CdmaSmsCbProgramData.ALERT_OPTION_DEFAULT_ALERT);
844 
845         bos.write(8, categoryNameLength);
846         for (int i = 0; i < categoryNameLength; i++) {
847             bos.write(7, CAT_EXTREME_THREAT.charAt(i));
848         }
849         bos.write(subparamPadBits, 0);
850 
851         SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
852         assertNotNull(msg);
853         msg.parseSms();
854         List<CdmaSmsCbProgramData> programDataList = msg.getSmsCbProgramData();
855         assertNotNull(programDataList);
856         assertEquals(1, programDataList.size());
857         CdmaSmsCbProgramData programData = programDataList.get(0);
858         assertEquals(CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY, programData.getOperation());
859         assertEquals(CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
860                 programData.getCategory());
861         assertEquals(CAT_EXTREME_THREAT, programData.getCategoryName());
862         assertEquals(BearerData.LANGUAGE_ENGLISH, programData.getLanguage());
863         assertEquals(100, programData.getMaxMessages());
864         assertEquals(CdmaSmsCbProgramData.ALERT_OPTION_DEFAULT_ALERT, programData.getAlertOption());
865     }
866 
867     @Test
testServiceCategoryProgramDataDeleteTwoCategories()868     public void testServiceCategoryProgramDataDeleteTwoCategories() throws Exception {
869         CdmaSmsMessage cdmaSmsMessage = createServiceCategoryProgramDataParcel();
870         BitwiseOutputStream bos = createBearerDataStream(456, -1, -1);
871 
872         int category1NameLength = CAT_SEVERE_THREAT.length();
873         int category2NameLength = CAT_AMBER_ALERTS.length();
874 
875         int subparamLengthBits = (101 + (category1NameLength * 7) + (category2NameLength * 7));
876         int subparamLengthBytes = (subparamLengthBits + 7) / 8;
877         int subparamPadBits = (subparamLengthBytes * 8) - subparamLengthBits;
878 
879         bos.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA);
880         bos.write(8, subparamLengthBytes);
881         bos.write(5, ENCODING_7BIT_ASCII);
882 
883         bos.write(4, CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY);
884         bos.write(8, (CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT >>> 8));
885         bos.write(8, (CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT & 0xff));
886         bos.write(8, BearerData.LANGUAGE_ENGLISH);
887         bos.write(8, 0);  // max messages
888         bos.write(4, CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT);
889 
890         bos.write(8, category1NameLength);
891         for (int i = 0; i < category1NameLength; i++) {
892             bos.write(7, CAT_SEVERE_THREAT.charAt(i));
893         }
894 
895         bos.write(4, CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY);
896         bos.write(8, (CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY >>> 8));
897         bos.write(8, (CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY & 0xff));
898         bos.write(8, BearerData.LANGUAGE_ENGLISH);
899         bos.write(8, 0);  // max messages
900         bos.write(4, CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT);
901 
902         bos.write(8, category2NameLength);
903         for (int i = 0; i < category2NameLength; i++) {
904             bos.write(7, CAT_AMBER_ALERTS.charAt(i));
905         }
906 
907         bos.write(subparamPadBits, 0);
908 
909         SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
910         assertNotNull(msg);
911         msg.parseSms();
912         List<CdmaSmsCbProgramData> programDataList = msg.getSmsCbProgramData();
913         assertNotNull(programDataList);
914         assertEquals(2, programDataList.size());
915 
916         CdmaSmsCbProgramData programData = programDataList.get(0);
917         assertEquals(CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY, programData.getOperation());
918         assertEquals(CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT,
919                 programData.getCategory());
920         assertEquals(CAT_SEVERE_THREAT, programData.getCategoryName());
921         assertEquals(BearerData.LANGUAGE_ENGLISH, programData.getLanguage());
922         assertEquals(0, programData.getMaxMessages());
923         assertEquals(CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT, programData.getAlertOption());
924 
925         programData = programDataList.get(1);
926         assertEquals(CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY, programData.getOperation());
927         assertEquals(CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY,
928                 programData.getCategory());
929         assertEquals(CAT_AMBER_ALERTS, programData.getCategoryName());
930         assertEquals(BearerData.LANGUAGE_ENGLISH, programData.getLanguage());
931         assertEquals(0, programData.getMaxMessages());
932         assertEquals(CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT, programData.getAlertOption());
933     }
934 
935     private static final byte[] CMAS_TEST_BEARER_DATA = {
936             0x00, 0x03, 0x1C, 0x78, 0x00, 0x01, 0x59, 0x02, (byte) 0xB8, 0x00, 0x02, 0x10,
937             (byte) 0xAA,
938             0x68, (byte) 0xD3, (byte) 0xCD, 0x06, (byte) 0x9E, 0x68, 0x30, (byte) 0xA0, (byte) 0xE9,
939             (byte) 0x97, (byte) 0x9F, 0x44, 0x1B, (byte) 0xF3, 0x20, (byte) 0xE9, (byte) 0xA3,
940             0x2A, 0x08, 0x7B, (byte) 0xF6, (byte) 0xED, (byte) 0xCB, (byte) 0xCB, 0x1E, (byte) 0x9C,
941             0x3B, 0x10, 0x4D, (byte) 0xDF, (byte) 0x8B, 0x4E,
942             (byte) 0xCC, (byte) 0xA8, 0x20, (byte) 0xEC, (byte) 0xCB, (byte) 0xCB, (byte) 0xA2,
943             0x0A,
944             0x7E, 0x79, (byte) 0xF4, (byte) 0xCB, (byte) 0xB5, 0x72, 0x0A, (byte) 0x9A, 0x34,
945             (byte) 0xF3, 0x41, (byte) 0xA7, (byte) 0x9A, 0x0D, (byte) 0xFB, (byte) 0xB6, 0x79, 0x41,
946             (byte) 0x85, 0x07, 0x4C, (byte) 0xBC, (byte) 0xFA, 0x2E, 0x00, 0x08, 0x20, 0x58, 0x38,
947             (byte) 0x88, (byte) 0x80, 0x10, 0x54, 0x06, 0x38, 0x20, 0x60,
948             0x30, (byte) 0xA8, (byte) 0x81, (byte) 0x90, 0x20, 0x08
949     };
950 
951     // Test case for CMAS test message received on the Sprint network.
952     @Test
testDecodeRawBearerData()953     public void testDecodeRawBearerData() {
954         CdmaSmsMessage cdmaSmsMessage =
955                 createBroadcastParcel(CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE);
956         SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, CMAS_TEST_BEARER_DATA);
957 
958         SmsCbMessage cbMessage =
959                 DefaultCellBroadcastService.parseCdmaBroadcastSms(mMockedContext,
960                         0, "", msg.getEnvelopeBearerData(), msg.getEnvelopeServiceCategory());
961         //SmsCbMessage cbMessage = msg.parseCdmaBroadcastSms("", 0);
962         assertNotNull("expected non-null for bearer data", cbMessage);
963         assertEquals("geoScope", cbMessage.getGeographicalScope(), 1);
964         assertEquals("serialNumber", cbMessage.getSerialNumber(), 51072);
965         assertEquals("serviceCategory", cbMessage.getServiceCategory(),
966                 CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE);
967         assertEquals("payload", cbMessage.getMessageBody(),
968                 "This is a test of the Commercial Mobile Alert System. This is only a test.");
969 
970         SmsCbCmasInfo cmasInfo = cbMessage.getCmasWarningInfo();
971         assertNotNull("expected non-null for CMAS info", cmasInfo);
972         assertEquals("category", cmasInfo.getCategory(), SmsCbCmasInfo.CMAS_CATEGORY_OTHER);
973         assertEquals("responseType", cmasInfo.getResponseType(),
974                 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_NONE);
975         assertEquals("severity", cmasInfo.getSeverity(), SmsCbCmasInfo.CMAS_SEVERITY_SEVERE);
976         assertEquals("urgency", cmasInfo.getUrgency(), SmsCbCmasInfo.CMAS_URGENCY_EXPECTED);
977         assertEquals("certainty", cmasInfo.getCertainty(), SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY);
978     }
979 }
980