Client (framework) side HAL changes for RIL unsolicited responses.
[android/platform/frameworks/opt/telephony.git] / tests / telephonytests / src / com / android / internal / telephony / cdma / CdmaSmsCbTest.java
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.internal.telephony.cdma;
18
19 import android.hardware.radio.V1_0.CdmaSmsMessage;
20 import android.os.Parcel;
21 import android.platform.test.annotations.Postsubmit;
22 import android.telephony.SmsCbCmasInfo;
23 import android.telephony.SmsCbMessage;
24 import android.telephony.cdma.CdmaSmsCbProgramData;
25 import android.test.AndroidTestCase;
26 import android.telephony.Rlog;
27 import android.test.suitebuilder.annotation.SmallTest;
28
29 import com.android.internal.telephony.GsmAlphabet;
30 import com.android.internal.telephony.cdma.sms.BearerData;
31 import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
32 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
33 import com.android.internal.telephony.cdma.sms.UserData;
34 import com.android.internal.telephony.uicc.IccUtils;
35 import com.android.internal.util.BitwiseOutputStream;
36
37 import org.junit.Test;
38
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.Random;
43
44 /**
45  * Test cases for basic SmsCbMessage operation for CDMA.
46  */
47 public class CdmaSmsCbTest extends AndroidTestCase {
48
49     /* Copy of private subparameter identifier constants from BearerData class. */
50     private static final byte SUBPARAM_MESSAGE_IDENTIFIER   = (byte) 0x00;
51     private static final byte SUBPARAM_USER_DATA            = (byte) 0x01;
52     private static final byte SUBPARAM_PRIORITY_INDICATOR   = (byte) 0x08;
53     private static final byte SUBPARAM_LANGUAGE_INDICATOR   = (byte) 0x0D;
54     private static final byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA    = 0x12;
55
56     /**
57      * Initialize a Parcel for an incoming CDMA cell broadcast. The caller will write the
58      * bearer data and then convert it to an SmsMessage.
59      * @param serviceCategory the CDMA service category
60      * @return the initialized Parcel
61      */
62     private static CdmaSmsMessage createBroadcastParcel(int serviceCategory) {
63         CdmaSmsMessage msg = new CdmaSmsMessage();
64
65         msg.teleserviceId = SmsEnvelope.TELESERVICE_NOT_SET;
66         msg.isServicePresent = true;
67         msg.serviceCategory = serviceCategory;
68
69         // dummy address (RIL may generate a different dummy address for broadcasts)
70         msg.address.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
71         msg.address.numberMode = CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK;
72         msg.address.numberType = CdmaSmsAddress.TON_UNKNOWN;
73         msg.address.numberPlan = CdmaSmsAddress.NUMBERING_PLAN_ISDN_TELEPHONY;
74         msg.subAddress.subaddressType = 0;
75         msg.subAddress.odd = false;
76         return msg;
77     }
78
79     /**
80      * Initialize a BitwiseOutputStream with the CDMA bearer data subparameters except for
81      * user data. The caller will append the user data and add it to the parcel.
82      * @param messageId the 16-bit message identifier
83      * @param priority message priority
84      * @param language message language code
85      * @return the initialized BitwiseOutputStream
86      */
87     private static BitwiseOutputStream createBearerDataStream(int messageId, int priority,
88             int language) throws BitwiseOutputStream.AccessException {
89         BitwiseOutputStream bos = new BitwiseOutputStream(10);
90         bos.write(8, SUBPARAM_MESSAGE_IDENTIFIER);
91         bos.write(8, 3);    // length: 3 bytes
92         bos.write(4, BearerData.MESSAGE_TYPE_DELIVER);
93         bos.write(8, ((messageId >>> 8) & 0xff));
94         bos.write(8, (messageId & 0xff));
95         bos.write(1, 0);    // no User Data Header
96         bos.write(3, 0);    // reserved
97
98         if (priority != -1) {
99             bos.write(8, SUBPARAM_PRIORITY_INDICATOR);
100             bos.write(8, 1);    // length: 1 byte
101             bos.write(2, (priority & 0x03));
102             bos.write(6, 0);    // reserved
103         }
104
105         if (language != -1) {
106             bos.write(8, SUBPARAM_LANGUAGE_INDICATOR);
107             bos.write(8, 1);    // length: 1 byte
108             bos.write(8, (language & 0xff));
109         }
110
111         return bos;
112     }
113
114     /**
115      * Write the bearer data array to the parcel, then return a new SmsMessage from the parcel.
116      * @param msg CdmaSmsMessage containing the CDMA SMS headers
117      * @param bearerData the bearer data byte array to append to the parcel
118      * @return the new SmsMessage created from the parcel
119      */
120     private static SmsMessage createMessageFromParcel(CdmaSmsMessage msg, byte[] bearerData) {
121         for (byte b : bearerData) {
122             msg.bearerData.add(b);
123         }
124         SmsMessage message = SmsMessage.newFromRil(msg);
125         return message;
126     }
127
128     /**
129      * Create a parcel for an incoming CMAS broadcast, then return a new SmsMessage created
130      * from the parcel.
131      * @param serviceCategory the CDMA service category
132      * @param messageId the 16-bit message identifier
133      * @param priority message priority
134      * @param language message language code
135      * @param body message body
136      * @param cmasCategory CMAS category (or -1 to skip adding CMAS type 1 elements record)
137      * @param responseType CMAS response type
138      * @param severity CMAS severity
139      * @param urgency CMAS urgency
140      * @param certainty CMAS certainty
141      * @return the newly created SmsMessage object
142      */
143     private static SmsMessage createCmasSmsMessage(int serviceCategory, int messageId, int priority,
144             int language, int encoding, String body, int cmasCategory, int responseType,
145             int severity, int urgency, int certainty) throws Exception {
146         BitwiseOutputStream cmasBos = new BitwiseOutputStream(10);
147         cmasBos.write(8, 0);    // CMAE protocol version 0
148
149         if (body != null) {
150             cmasBos.write(8, 0);        // Type 0 elements (alert text)
151             encodeBody(encoding, body, true, cmasBos);
152         }
153
154         if (cmasCategory != SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN) {
155             cmasBos.write(8, 1);    // Type 1 elements
156             cmasBos.write(8, 4);    // length: 4 bytes
157             cmasBos.write(8, (cmasCategory & 0xff));
158             cmasBos.write(8, (responseType & 0xff));
159             cmasBos.write(4, (severity & 0x0f));
160             cmasBos.write(4, (urgency & 0x0f));
161             cmasBos.write(4, (certainty & 0x0f));
162             cmasBos.write(4, 0);    // pad to octet boundary
163         }
164
165         byte[] cmasUserData = cmasBos.toByteArray();
166
167         CdmaSmsMessage msg = createBroadcastParcel(serviceCategory);
168         BitwiseOutputStream bos = createBearerDataStream(messageId, priority, language);
169
170         bos.write(8, SUBPARAM_USER_DATA);
171         bos.write(8, cmasUserData.length + 2);  // add 2 bytes for msg_encoding and num_fields
172         bos.write(5, UserData.ENCODING_OCTET);
173         bos.write(8, cmasUserData.length);
174         bos.writeByteArray(cmasUserData.length * 8, cmasUserData);
175         bos.write(3, 0);    // pad to byte boundary
176
177         return createMessageFromParcel(msg, bos.toByteArray());
178     }
179
180     /**
181      * Create a parcel for an incoming CDMA cell broadcast, then return a new SmsMessage created
182      * from the parcel.
183      * @param serviceCategory the CDMA service category
184      * @param messageId the 16-bit message identifier
185      * @param priority message priority
186      * @param language message language code
187      * @param encoding user data encoding method
188      * @param body the message body
189      * @return the newly created SmsMessage object
190      */
191     private static SmsMessage createBroadcastSmsMessage(int serviceCategory, int messageId,
192             int priority, int language, int encoding, String body) throws Exception {
193         CdmaSmsMessage msg = createBroadcastParcel(serviceCategory);
194         BitwiseOutputStream bos = createBearerDataStream(messageId, priority, language);
195
196         bos.write(8, SUBPARAM_USER_DATA);
197         encodeBody(encoding, body, false, bos);
198
199         return createMessageFromParcel(msg, bos.toByteArray());
200     }
201
202     /**
203      * Append the message length, encoding, and body to the BearerData output stream.
204      * This is used for writing the User Data subparameter for non-CMAS broadcasts and for
205      * writing the alert text for CMAS broadcasts.
206      * @param encoding one of the CDMA UserData encoding values
207      * @param body the message body
208      * @param isCmasRecord true if this is a CMAS type 0 elements record; false for user data
209      * @param bos the BitwiseOutputStream to write to
210      * @throws Exception on any encoding error
211      */
212     private static void encodeBody(int encoding, String body, boolean isCmasRecord,
213             BitwiseOutputStream bos) throws Exception {
214         if (encoding == UserData.ENCODING_7BIT_ASCII || encoding == UserData.ENCODING_IA5) {
215             int charCount = body.length();
216             int recordBits = (charCount * 7) + 5;       // add 5 bits for char set field
217             int recordOctets = (recordBits + 7) / 8;    // round up to octet boundary
218             int padBits = (recordOctets * 8) - recordBits;
219
220             if (!isCmasRecord) {
221                 recordOctets++;                         // add 8 bits for num_fields
222             }
223
224             bos.write(8, recordOctets);
225             bos.write(5, (encoding & 0x1f));
226
227             if (!isCmasRecord) {
228                 bos.write(8, charCount);
229             }
230
231             for (int i = 0; i < charCount; i++) {
232                 bos.write(7, body.charAt(i));
233             }
234
235             bos.write(padBits, 0);      // pad to octet boundary
236         } else if (encoding == UserData.ENCODING_GSM_7BIT_ALPHABET
237                 || encoding == UserData.ENCODING_GSM_DCS) {
238             // convert to 7-bit packed encoding with septet count in index 0 of byte array
239             byte[] encodedBody = GsmAlphabet.stringToGsm7BitPacked(body);
240
241             int charCount = encodedBody[0];             // septet count
242             int recordBits = (charCount * 7) + 5;       // add 5 bits for char set field
243             int recordOctets = (recordBits + 7) / 8;    // round up to octet boundary
244             int padBits = (recordOctets * 8) - recordBits;
245
246             if (!isCmasRecord) {
247                 recordOctets++;                         // add 8 bits for num_fields
248                 if (encoding == UserData.ENCODING_GSM_DCS) {
249                     recordOctets++;                     // add 8 bits for DCS (message type)
250                 }
251             }
252
253             bos.write(8, recordOctets);
254             bos.write(5, (encoding & 0x1f));
255
256             if (!isCmasRecord && encoding == UserData.ENCODING_GSM_DCS) {
257                 bos.write(8, 0);        // GSM DCS: 7 bit default alphabet, no msg class
258             }
259
260             if (!isCmasRecord) {
261                 bos.write(8, charCount);
262             }
263             byte[] bodySeptets = Arrays.copyOfRange(encodedBody, 1, encodedBody.length);
264             bos.writeByteArray(charCount * 7, bodySeptets);
265             bos.write(padBits, 0);      // pad to octet boundary
266         } else if (encoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
267             // 6 bit packed encoding with 0x20 offset (ASCII 0x20 - 0x60)
268             int charCount = body.length();
269             int recordBits = (charCount * 6) + 21;      // add 21 bits for header fields
270             int recordOctets = (recordBits + 7) / 8;    // round up to octet boundary
271             int padBits = (recordOctets * 8) - recordBits;
272
273             bos.write(8, recordOctets);
274
275             bos.write(5, (encoding & 0x1f));
276             bos.write(8, UserData.IS91_MSG_TYPE_SHORT_MESSAGE);
277             bos.write(8, charCount);
278
279             for (int i = 0; i < charCount; i++) {
280                 bos.write(6, ((int) body.charAt(i) - 0x20));
281             }
282
283             bos.write(padBits, 0);      // pad to octet boundary
284         } else {
285             byte[] encodedBody;
286             switch (encoding) {
287                 case UserData.ENCODING_UNICODE_16:
288                     encodedBody = body.getBytes("UTF-16BE");
289                     break;
290
291                 case UserData.ENCODING_SHIFT_JIS:
292                     encodedBody = body.getBytes("Shift_JIS");
293                     break;
294
295                 case UserData.ENCODING_KOREAN:
296                     encodedBody = body.getBytes("KSC5601");
297                     break;
298
299                 case UserData.ENCODING_LATIN_HEBREW:
300                     encodedBody = body.getBytes("ISO-8859-8");
301                     break;
302
303                 case UserData.ENCODING_LATIN:
304                 default:
305                     encodedBody = body.getBytes("ISO-8859-1");
306                     break;
307             }
308             int charCount = body.length();              // use actual char count for num fields
309             int recordOctets = encodedBody.length + 1;  // add 1 byte for encoding and pad bits
310             if (!isCmasRecord) {
311                 recordOctets++;                         // add 8 bits for num_fields
312             }
313             bos.write(8, recordOctets);
314             bos.write(5, (encoding & 0x1f));
315             if (!isCmasRecord) {
316                 bos.write(8, charCount);
317             }
318             bos.writeByteArray(encodedBody.length * 8, encodedBody);
319             bos.write(3, 0);            // pad to octet boundary
320         }
321     }
322
323     private static final String TEST_TEXT = "This is a test CDMA cell broadcast message..."
324             + "678901234567890123456789012345678901234567890";
325
326     private static final String PRES_ALERT =
327             "THE PRESIDENT HAS ISSUED AN EMERGENCY ALERT. CHECK LOCAL MEDIA FOR MORE DETAILS";
328
329     private static final String EXTREME_ALERT = "FLASH FLOOD WARNING FOR SOUTH COCONINO COUNTY"
330             + " - NORTH CENTRAL ARIZONA UNTIL 415 PM MST";
331
332     private static final String SEVERE_ALERT = "SEVERE WEATHER WARNING FOR SOMERSET COUNTY"
333             + " - NEW JERSEY UNTIL 415 PM MST";
334
335     private static final String AMBER_ALERT =
336             "AMBER ALERT:Mountain View,CA VEH'07 Blue Honda Civic CA LIC 5ABC123";
337
338     private static final String MONTHLY_TEST_ALERT = "This is a test of the emergency alert system."
339             + " This is only a test. 89012345678901234567890";
340
341     private static final String IS91_TEXT = "IS91 SHORT MSG";   // max length 14 chars
342
343     /**
344      * Verify that the SmsCbMessage has the correct values for CDMA.
345      * @param cbMessage the message to test
346      */
347     private static void verifyCbValues(SmsCbMessage cbMessage) {
348         assertEquals(SmsCbMessage.MESSAGE_FORMAT_3GPP2, cbMessage.getMessageFormat());
349         assertEquals(SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE, cbMessage.getGeographicalScope());
350         assertEquals(false, cbMessage.isEtwsMessage()); // ETWS on CDMA not currently supported
351     }
352
353     private static void doTestNonEmergencyBroadcast(int encoding) throws Exception {
354         SmsMessage msg = createBroadcastSmsMessage(123, 456, BearerData.PRIORITY_NORMAL,
355                 BearerData.LANGUAGE_ENGLISH, encoding, TEST_TEXT);
356
357         SmsCbMessage cbMessage = msg.parseBroadcastSms();
358         verifyCbValues(cbMessage);
359         assertEquals(123, cbMessage.getServiceCategory());
360         assertEquals(456, cbMessage.getSerialNumber());
361         assertEquals(SmsCbMessage.MESSAGE_PRIORITY_NORMAL, cbMessage.getMessagePriority());
362         assertEquals("en", cbMessage.getLanguageCode());
363         assertEquals(TEST_TEXT, cbMessage.getMessageBody());
364         assertEquals(false, cbMessage.isEmergencyMessage());
365         assertEquals(false, cbMessage.isCmasMessage());
366     }
367
368     @Test @SmallTest
369     public void testNonEmergencyBroadcast7bitAscii() throws Exception {
370         doTestNonEmergencyBroadcast(UserData.ENCODING_7BIT_ASCII);
371     }
372
373     @Test @SmallTest
374     public void testNonEmergencyBroadcast7bitGsm() throws Exception {
375         doTestNonEmergencyBroadcast(UserData.ENCODING_GSM_7BIT_ALPHABET);
376     }
377
378     @Test @SmallTest
379     public void testNonEmergencyBroadcast16bitUnicode() throws Exception {
380         doTestNonEmergencyBroadcast(UserData.ENCODING_UNICODE_16);
381     }
382
383     @Test @SmallTest
384     public void testNonEmergencyBroadcastIs91Extended() throws Exception {
385         // IS-91 doesn't support language or priority subparameters, max 14 chars text
386         SmsMessage msg = createBroadcastSmsMessage(987, 654, -1, -1,
387                 UserData.ENCODING_IS91_EXTENDED_PROTOCOL, IS91_TEXT);
388
389         SmsCbMessage cbMessage = msg.parseBroadcastSms();
390         verifyCbValues(cbMessage);
391         assertEquals(987, cbMessage.getServiceCategory());
392         assertEquals(654, cbMessage.getSerialNumber());
393         assertEquals(SmsCbMessage.MESSAGE_PRIORITY_NORMAL, cbMessage.getMessagePriority());
394         assertEquals(null, cbMessage.getLanguageCode());
395         assertEquals(IS91_TEXT, cbMessage.getMessageBody());
396         assertEquals(false, cbMessage.isEmergencyMessage());
397         assertEquals(false, cbMessage.isCmasMessage());
398     }
399
400     private static void doTestCmasBroadcast(int serviceCategory, int messageClass, String body)
401             throws Exception {
402         SmsMessage msg = createCmasSmsMessage(
403                 serviceCategory, 1234, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
404                 UserData.ENCODING_7BIT_ASCII, body, -1, -1, -1, -1, -1);
405
406         SmsCbMessage cbMessage = msg.parseBroadcastSms();
407         verifyCbValues(cbMessage);
408         assertEquals(serviceCategory, cbMessage.getServiceCategory());
409         assertEquals(1234, cbMessage.getSerialNumber());
410         assertEquals(SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, cbMessage.getMessagePriority());
411         assertEquals("en", cbMessage.getLanguageCode());
412         assertEquals(body, cbMessage.getMessageBody());
413         assertEquals(true, cbMessage.isEmergencyMessage());
414         assertEquals(true, cbMessage.isCmasMessage());
415         SmsCbCmasInfo cmasInfo = cbMessage.getCmasWarningInfo();
416         assertEquals(messageClass, cmasInfo.getMessageClass());
417         assertEquals(SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN, cmasInfo.getCategory());
418         assertEquals(SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, cmasInfo.getResponseType());
419         assertEquals(SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN, cmasInfo.getSeverity());
420         assertEquals(SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN, cmasInfo.getUrgency());
421         assertEquals(SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN, cmasInfo.getCertainty());
422     }
423
424     @Test @SmallTest
425     public void testCmasPresidentialAlert() throws Exception {
426         doTestCmasBroadcast(SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
427                 SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT, PRES_ALERT);
428     }
429
430     @Test @SmallTest
431     public void testCmasExtremeAlert() throws Exception {
432         doTestCmasBroadcast(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT,
433                 SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT, EXTREME_ALERT);
434     }
435
436     @Test @SmallTest
437     public void testCmasSevereAlert() throws Exception {
438         doTestCmasBroadcast(SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT,
439                 SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT, SEVERE_ALERT);
440     }
441
442     @Test @SmallTest
443     public void testCmasAmberAlert() throws Exception {
444         doTestCmasBroadcast(SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY,
445                 SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY, AMBER_ALERT);
446     }
447
448     @Test @SmallTest
449     public void testCmasTestMessage() throws Exception {
450         doTestCmasBroadcast(SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE,
451                 SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST, MONTHLY_TEST_ALERT);
452     }
453
454     @Test @SmallTest
455     public void testCmasExtremeAlertType1Elements() throws Exception {
456         SmsMessage msg = createCmasSmsMessage(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT,
457                 5678, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
458                 UserData.ENCODING_7BIT_ASCII, EXTREME_ALERT, SmsCbCmasInfo.CMAS_CATEGORY_ENV,
459                 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR, SmsCbCmasInfo.CMAS_SEVERITY_SEVERE,
460                 SmsCbCmasInfo.CMAS_URGENCY_EXPECTED, SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY);
461
462         SmsCbMessage cbMessage = msg.parseBroadcastSms();
463         verifyCbValues(cbMessage);
464         assertEquals(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT,
465                 cbMessage.getServiceCategory());
466         assertEquals(5678, cbMessage.getSerialNumber());
467         assertEquals(SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, cbMessage.getMessagePriority());
468         assertEquals("en", cbMessage.getLanguageCode());
469         assertEquals(EXTREME_ALERT, cbMessage.getMessageBody());
470         assertEquals(true, cbMessage.isEmergencyMessage());
471         assertEquals(true, cbMessage.isCmasMessage());
472         SmsCbCmasInfo cmasInfo = cbMessage.getCmasWarningInfo();
473         assertEquals(SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT, cmasInfo.getMessageClass());
474         assertEquals(SmsCbCmasInfo.CMAS_CATEGORY_ENV, cmasInfo.getCategory());
475         assertEquals(SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR, cmasInfo.getResponseType());
476         assertEquals(SmsCbCmasInfo.CMAS_SEVERITY_SEVERE, cmasInfo.getSeverity());
477         assertEquals(SmsCbCmasInfo.CMAS_URGENCY_EXPECTED, cmasInfo.getUrgency());
478         assertEquals(SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY, cmasInfo.getCertainty());
479     }
480
481     // VZW requirement is to discard message with unsupported charset. Verify that we return null
482     // for this unsupported character set.
483     @Postsubmit
484     @Test @SmallTest
485     public void testCmasUnsupportedCharSet() throws Exception {
486         SmsMessage msg = createCmasSmsMessage(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT,
487                 12345, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
488                 UserData.ENCODING_GSM_DCS, EXTREME_ALERT, -1, -1, -1, -1, -1);
489
490         SmsCbMessage cbMessage = msg.parseBroadcastSms();
491         assertNull("expected null for unsupported charset", cbMessage);
492     }
493
494     // VZW requirement is to discard message with unsupported charset. Verify that we return null
495     // for this unsupported character set.
496     @Test @SmallTest
497     public void testCmasUnsupportedCharSet2() throws Exception {
498         SmsMessage msg = createCmasSmsMessage(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT,
499                 67890, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
500                 UserData.ENCODING_KOREAN, EXTREME_ALERT, -1, -1, -1, -1, -1);
501
502         SmsCbMessage cbMessage = msg.parseBroadcastSms();
503         assertNull("expected null for unsupported charset", cbMessage);
504     }
505
506     // VZW requirement is to discard message without record type 0. The framework will decode it
507     // and the app will discard it.
508     @Test @SmallTest
509     public void testCmasNoRecordType0() throws Exception {
510         SmsMessage msg = createCmasSmsMessage(
511                 SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT, 1234,
512                 BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
513                 UserData.ENCODING_7BIT_ASCII, null, -1, -1, -1, -1, -1);
514
515         SmsCbMessage cbMessage = msg.parseBroadcastSms();
516         verifyCbValues(cbMessage);
517         assertEquals(SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
518                 cbMessage.getServiceCategory());
519         assertEquals(1234, cbMessage.getSerialNumber());
520         assertEquals(SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, cbMessage.getMessagePriority());
521         assertEquals("en", cbMessage.getLanguageCode());
522         assertEquals(null, cbMessage.getMessageBody());
523         assertEquals(true, cbMessage.isEmergencyMessage());
524         assertEquals(true, cbMessage.isCmasMessage());
525         SmsCbCmasInfo cmasInfo = cbMessage.getCmasWarningInfo();
526         assertEquals(SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT, cmasInfo.getMessageClass());
527         assertEquals(SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN, cmasInfo.getCategory());
528         assertEquals(SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, cmasInfo.getResponseType());
529         assertEquals(SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN, cmasInfo.getSeverity());
530         assertEquals(SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN, cmasInfo.getUrgency());
531         assertEquals(SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN, cmasInfo.getCertainty());
532     }
533
534     // Make sure we don't throw an exception if we feed completely random data to BearerStream.
535     @Test @SmallTest
536     public void testRandomBearerStreamData() {
537         Random r = new Random(54321);
538         for (int run = 0; run < 1000; run++) {
539             int len = r.nextInt(140);
540             byte[] data = new byte[len];
541             for (int i = 0; i < len; i++) {
542                 data[i] = (byte) r.nextInt(256);
543             }
544             // Rlog.d("CdmaSmsCbTest", "trying random bearer data run " + run + " length " + len);
545             try {
546                 int category = 0x0ff0 + r.nextInt(32);  // half CMAS, half non-CMAS
547                 CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(category);
548                 SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, data);
549                 SmsCbMessage cbMessage = msg.parseBroadcastSms();
550                 // with random input, cbMessage will almost always be null (log when it isn't)
551                 if (cbMessage != null) {
552                     Rlog.d("CdmaSmsCbTest", "success: " + cbMessage);
553                 }
554             } catch (Exception e) {
555                 Rlog.d("CdmaSmsCbTest", "exception thrown", e);
556                 fail("Exception in decoder at run " + run + " length " + len + ": " + e);
557             }
558         }
559     }
560
561     // Make sure we don't throw an exception if we put random data in the UserData subparam.
562     @Test @SmallTest
563     public void testRandomUserData() {
564         Random r = new Random(94040);
565         for (int run = 0; run < 1000; run++) {
566             int category = 0x0ff0 + r.nextInt(32);  // half CMAS, half non-CMAS
567             CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(category);
568             int len = r.nextInt(140);
569             // Rlog.d("CdmaSmsCbTest", "trying random user data run " + run + " length " + len);
570
571             try {
572                 BitwiseOutputStream bos = createBearerDataStream(r.nextInt(65536), r.nextInt(4),
573                         r.nextInt(256));
574
575                 bos.write(8, SUBPARAM_USER_DATA);
576                 bos.write(8, len);
577
578                 for (int i = 0; i < len; i++) {
579                     bos.write(8, r.nextInt(256));
580                 }
581
582                 SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
583                 SmsCbMessage cbMessage = msg.parseBroadcastSms();
584             } catch (Exception e) {
585                 Rlog.d("CdmaSmsCbTest", "exception thrown", e);
586                 fail("Exception in decoder at run " + run + " length " + len + ": " + e);
587             }
588         }
589     }
590
591     /**
592      * Initialize a Parcel for incoming Service Category Program Data teleservice. The caller will
593      * write the bearer data and then convert it to an SmsMessage.
594      * @return the initialized Parcel
595      */
596     private static CdmaSmsMessage createServiceCategoryProgramDataParcel() {
597         CdmaSmsMessage msg = new CdmaSmsMessage();
598
599         msg.teleserviceId = SmsEnvelope.TELESERVICE_SCPT;
600         msg.isServicePresent = false;
601         msg.serviceCategory = 0;
602
603         // dummy address (RIL may generate a different dummy address for broadcasts)
604         msg.address.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
605         msg.address.numberMode = CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK;
606         msg.address.numberType = CdmaSmsAddress.TON_UNKNOWN;
607         msg.address.numberPlan = CdmaSmsAddress.NUMBERING_PLAN_ISDN_TELEPHONY;
608         msg.subAddress.subaddressType = 0;
609         msg.subAddress.odd = false;
610         return msg;
611     }
612
613     private static final String CAT_EXTREME_THREAT = "Extreme Threat to Life and Property";
614     private static final String CAT_SEVERE_THREAT = "Severe Threat to Life and Property";
615     private static final String CAT_AMBER_ALERTS = "AMBER Alerts";
616
617     @Test @SmallTest
618     public void testServiceCategoryProgramDataAddCategory() throws Exception {
619         CdmaSmsMessage cdmaSmsMessage = createServiceCategoryProgramDataParcel();
620         BitwiseOutputStream bos = createBearerDataStream(123, -1, -1);
621
622         int categoryNameLength = CAT_EXTREME_THREAT.length();
623         int subparamLengthBits = (53 + (categoryNameLength * 7));
624         int subparamLengthBytes = (subparamLengthBits + 7) / 8;
625         int subparamPadBits = (subparamLengthBytes * 8) - subparamLengthBits;
626
627         bos.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA);
628         bos.write(8, subparamLengthBytes);
629         bos.write(5, UserData.ENCODING_7BIT_ASCII);
630
631         bos.write(4, CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY);
632         bos.write(8, (SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT >>> 8));
633         bos.write(8, (SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT & 0xff));
634         bos.write(8, BearerData.LANGUAGE_ENGLISH);
635         bos.write(8, 100);  // max messages
636         bos.write(4, CdmaSmsCbProgramData.ALERT_OPTION_DEFAULT_ALERT);
637
638         bos.write(8, categoryNameLength);
639         for (int i = 0; i < categoryNameLength; i++) {
640             bos.write(7, CAT_EXTREME_THREAT.charAt(i));
641         }
642         bos.write(subparamPadBits, 0);
643
644         SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
645         assertNotNull(msg);
646         msg.parseSms();
647         List<CdmaSmsCbProgramData> programDataList = msg.getSmsCbProgramData();
648         assertNotNull(programDataList);
649         assertEquals(1, programDataList.size());
650         CdmaSmsCbProgramData programData = programDataList.get(0);
651         assertEquals(CdmaSmsCbProgramData.OPERATION_ADD_CATEGORY, programData.getOperation());
652         assertEquals(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, programData.getCategory());
653         assertEquals(CAT_EXTREME_THREAT, programData.getCategoryName());
654         assertEquals(BearerData.LANGUAGE_ENGLISH, programData.getLanguage());
655         assertEquals(100, programData.getMaxMessages());
656         assertEquals(CdmaSmsCbProgramData.ALERT_OPTION_DEFAULT_ALERT, programData.getAlertOption());
657     }
658
659     @Test @SmallTest
660     public void testServiceCategoryProgramDataDeleteTwoCategories() throws Exception {
661         CdmaSmsMessage cdmaSmsMessage = createServiceCategoryProgramDataParcel();
662         BitwiseOutputStream bos = createBearerDataStream(456, -1, -1);
663
664         int category1NameLength = CAT_SEVERE_THREAT.length();
665         int category2NameLength = CAT_AMBER_ALERTS.length();
666
667         int subparamLengthBits = (101 + (category1NameLength * 7) + (category2NameLength * 7));
668         int subparamLengthBytes = (subparamLengthBits + 7) / 8;
669         int subparamPadBits = (subparamLengthBytes * 8) - subparamLengthBits;
670
671         bos.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA);
672         bos.write(8, subparamLengthBytes);
673         bos.write(5, UserData.ENCODING_7BIT_ASCII);
674
675         bos.write(4, CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY);
676         bos.write(8, (SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT >>> 8));
677         bos.write(8, (SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT & 0xff));
678         bos.write(8, BearerData.LANGUAGE_ENGLISH);
679         bos.write(8, 0);  // max messages
680         bos.write(4, CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT);
681
682         bos.write(8, category1NameLength);
683         for (int i = 0; i < category1NameLength; i++) {
684             bos.write(7, CAT_SEVERE_THREAT.charAt(i));
685         }
686
687         bos.write(4, CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY);
688         bos.write(8, (SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY >>> 8));
689         bos.write(8, (SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY & 0xff));
690         bos.write(8, BearerData.LANGUAGE_ENGLISH);
691         bos.write(8, 0);  // max messages
692         bos.write(4, CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT);
693
694         bos.write(8, category2NameLength);
695         for (int i = 0; i < category2NameLength; i++) {
696             bos.write(7, CAT_AMBER_ALERTS.charAt(i));
697         }
698
699         bos.write(subparamPadBits, 0);
700
701         SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
702         assertNotNull(msg);
703         msg.parseSms();
704         List<CdmaSmsCbProgramData> programDataList = msg.getSmsCbProgramData();
705         assertNotNull(programDataList);
706         assertEquals(2, programDataList.size());
707
708         CdmaSmsCbProgramData programData = programDataList.get(0);
709         assertEquals(CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY, programData.getOperation());
710         assertEquals(SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT, programData.getCategory());
711         assertEquals(CAT_SEVERE_THREAT, programData.getCategoryName());
712         assertEquals(BearerData.LANGUAGE_ENGLISH, programData.getLanguage());
713         assertEquals(0, programData.getMaxMessages());
714         assertEquals(CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT, programData.getAlertOption());
715
716         programData = programDataList.get(1);
717         assertEquals(CdmaSmsCbProgramData.OPERATION_DELETE_CATEGORY, programData.getOperation());
718         assertEquals(SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY,
719                 programData.getCategory());
720         assertEquals(CAT_AMBER_ALERTS, programData.getCategoryName());
721         assertEquals(BearerData.LANGUAGE_ENGLISH, programData.getLanguage());
722         assertEquals(0, programData.getMaxMessages());
723         assertEquals(CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT, programData.getAlertOption());
724     }
725
726     private static final byte[] CMAS_TEST_BEARER_DATA = {
727         0x00, 0x03, 0x1C, 0x78, 0x00, 0x01, 0x59, 0x02, (byte) 0xB8, 0x00, 0x02, 0x10, (byte) 0xAA,
728         0x68, (byte) 0xD3, (byte) 0xCD, 0x06, (byte) 0x9E, 0x68, 0x30, (byte) 0xA0, (byte) 0xE9,
729         (byte) 0x97, (byte) 0x9F, 0x44, 0x1B, (byte) 0xF3, 0x20, (byte) 0xE9, (byte) 0xA3,
730         0x2A, 0x08, 0x7B, (byte) 0xF6, (byte) 0xED, (byte) 0xCB, (byte) 0xCB, 0x1E, (byte) 0x9C,
731         0x3B, 0x10, 0x4D, (byte) 0xDF, (byte) 0x8B, 0x4E,
732         (byte) 0xCC, (byte) 0xA8, 0x20, (byte) 0xEC, (byte) 0xCB, (byte) 0xCB, (byte) 0xA2, 0x0A,
733         0x7E, 0x79, (byte) 0xF4, (byte) 0xCB, (byte) 0xB5, 0x72, 0x0A, (byte) 0x9A, 0x34,
734         (byte) 0xF3, 0x41, (byte) 0xA7, (byte) 0x9A, 0x0D, (byte) 0xFB, (byte) 0xB6, 0x79, 0x41,
735         (byte) 0x85, 0x07, 0x4C, (byte) 0xBC, (byte) 0xFA, 0x2E, 0x00, 0x08, 0x20, 0x58, 0x38,
736         (byte) 0x88, (byte) 0x80, 0x10, 0x54, 0x06, 0x38, 0x20, 0x60,
737         0x30, (byte) 0xA8, (byte) 0x81, (byte) 0x90, 0x20, 0x08
738     };
739
740     // Test case for CMAS test message received on the Sprint network.
741     @Test @SmallTest
742     public void testDecodeRawBearerData() throws Exception {
743         CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE);
744         SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, CMAS_TEST_BEARER_DATA);
745
746         SmsCbMessage cbMessage = msg.parseBroadcastSms();
747         assertNotNull("expected non-null for bearer data", cbMessage);
748         assertEquals("geoScope", cbMessage.getGeographicalScope(), 1);
749         assertEquals("serialNumber", cbMessage.getSerialNumber(), 51072);
750         assertEquals("serviceCategory", cbMessage.getServiceCategory(),
751                 SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE);
752         assertEquals("payload", cbMessage.getMessageBody(),
753                 "This is a test of the Commercial Mobile Alert System. This is only a test.");
754
755         SmsCbCmasInfo cmasInfo = cbMessage.getCmasWarningInfo();
756         assertNotNull("expected non-null for CMAS info", cmasInfo);
757         assertEquals("category", cmasInfo.getCategory(), SmsCbCmasInfo.CMAS_CATEGORY_OTHER);
758         assertEquals("responseType", cmasInfo.getResponseType(),
759                 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_NONE);
760         assertEquals("severity", cmasInfo.getSeverity(), SmsCbCmasInfo.CMAS_SEVERITY_SEVERE);
761         assertEquals("urgency", cmasInfo.getUrgency(), SmsCbCmasInfo.CMAS_URGENCY_EXPECTED);
762         assertEquals("certainty", cmasInfo.getCertainty(), SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY);
763     }
764 }