1 /*
2  * Copyright (C) 2020 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.cellbroadcastreceiver.unit;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotEquals;
21 import static org.junit.Assert.assertNotNull;
22 import static org.mockito.ArgumentMatchers.any;
23 import static org.mockito.ArgumentMatchers.anyInt;
24 import static org.mockito.ArgumentMatchers.anyString;
25 import static org.mockito.ArgumentMatchers.eq;
26 import static org.mockito.Mockito.doReturn;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.times;
29 import static org.mockito.Mockito.verify;
30 
31 import android.content.Context;
32 import android.content.res.Configuration;
33 import android.content.res.Resources;
34 import android.telephony.SmsCbCmasInfo;
35 import android.telephony.SmsCbEtwsInfo;
36 import android.telephony.SmsCbLocation;
37 import android.telephony.SmsCbMessage;
38 import android.telephony.SubscriptionInfo;
39 import android.telephony.SubscriptionManager;
40 import android.telephony.TelephonyManager;
41 
42 import com.android.cellbroadcastreceiver.CellBroadcastChannelManager;
43 import com.android.cellbroadcastreceiver.CellBroadcastResources;
44 import com.android.cellbroadcastreceiver.R;
45 import com.android.internal.telephony.gsm.SmsCbConstants;
46 
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.mockito.Mock;
50 import org.mockito.MockitoAnnotations;
51 
52 import java.lang.reflect.Method;
53 
54 public class CellBroadcastResourcesTest {
55 
56     @Mock
57     private Context mContext;
58 
59     @Mock
60     private Resources mResources;
61 
62     @Before
setUp()63     public void setUp() throws Exception {
64         MockitoAnnotations.initMocks(this);
65         doReturn(mResources).when(mContext).getResources();
66         String stringResultToReturn = "";
67         doReturn(stringResultToReturn).when(mResources).getString(anyInt());
68         CellBroadcastChannelManager.clearAllCellBroadcastChannelRanges();
69     }
70 
71     @Test
testGetMessageDetails()72     public void testGetMessageDetails() {
73         SmsCbMessage smsCbMessage = new SmsCbMessage(1, 2, 0, new SmsCbLocation(),
74                 SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING, "language", "body",
75                 SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, null, new SmsCbCmasInfo(0, 2, 3, 4, 5, 6),
76                 0, 1);
77         CharSequence details = CellBroadcastResources.getMessageDetails(mContext, true,
78                 smsCbMessage, -1, false,
79                 null);
80         assertNotNull(details);
81     }
82 
83     @Test
testGetMessageDetailsCmasMessage()84     public void testGetMessageDetailsCmasMessage() {
85         SmsCbMessage smsCbMessage = new SmsCbMessage(1, 2, 0, new SmsCbLocation(),
86                 SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL, "language", "body",
87                 SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, null, new SmsCbCmasInfo(0, 2, 3, 4, 5, 6),
88                 0, 1);
89         CharSequence details = CellBroadcastResources.getMessageDetails(mContext, true,
90                 smsCbMessage, -1, false,
91                 null);
92         assertNotNull(details);
93     }
94 
95     @Test
testGetCmasCategoryResId()96     public void testGetCmasCategoryResId() throws Exception {
97         int[] cats = {SmsCbCmasInfo.CMAS_CATEGORY_GEO, SmsCbCmasInfo.CMAS_CATEGORY_MET,
98                 SmsCbCmasInfo.CMAS_CATEGORY_SAFETY, SmsCbCmasInfo.CMAS_CATEGORY_SECURITY,
99                 SmsCbCmasInfo.CMAS_CATEGORY_RESCUE, SmsCbCmasInfo.CMAS_CATEGORY_FIRE,
100                 SmsCbCmasInfo.CMAS_CATEGORY_HEALTH, SmsCbCmasInfo.CMAS_CATEGORY_ENV,
101                 SmsCbCmasInfo.CMAS_CATEGORY_TRANSPORT, SmsCbCmasInfo.CMAS_CATEGORY_INFRA,
102                 SmsCbCmasInfo.CMAS_CATEGORY_CBRNE, SmsCbCmasInfo.CMAS_CATEGORY_OTHER};
103         for (int c : cats) {
104             assertNotEquals(0, getCmasCategoryResId(new SmsCbCmasInfo(0, c, 0, 0, 0, 0)));
105         }
106 
107         assertEquals(0, getCmasCategoryResId(new SmsCbCmasInfo(
108                 0, SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN, 0, 0, 0, 0)));
109     }
110 
111     @Test
testGetDialogTitleResource()112     public void testGetDialogTitleResource() throws Exception {
113         mockSubscriptionManager();
114         Context mockContext2 = mock(Context.class);
115         doReturn(mResources).when(mockContext2).getResources();
116         Configuration config = new Configuration();
117         doReturn(config).when(mResources).getConfiguration();
118         doReturn(mockContext2).when(mContext).createConfigurationContext(any());
119 
120         setFakeSharedPreferences();
121         putResources(R.array.cmas_alert_extreme_channels_range_strings, new String[]{
122                     "0x1113-0x1114:rat=gsm",
123                     "0x1001-0x1001:rat=cdma",
124                     "0x1120-0x1121:rat=gsm",
125                 });
126         putResources(R.array.public_safety_messages_channels_range_strings, new String[]{
127                     "0x112C:rat=gsm, emergency=true",
128                     "0x112D:rat=gsm, emergency=true",
129                 });
130         SmsCbMessage smsCbMessage = new SmsCbMessage(1, 2, 0, new SmsCbLocation(),
131                 SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED, "language", "body",
132                 SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, null, new SmsCbCmasInfo(0, 2, 3, 4, 5, 6),
133                 0, 1);
134         int expectedResult = getDialogTitleResource(mContext, smsCbMessage);
135 
136         SmsCbMessage testSmsCbMessage = new SmsCbMessage(1, 2, 0, new SmsCbLocation(),
137                 SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED, "language", "body",
138                 SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, null, new SmsCbCmasInfo(0, 2, 3,
139                 SmsCbCmasInfo.CMAS_SEVERITY_EXTREME, SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE,
140                 SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY), 0, 1);
141 
142         int result = getDialogTitleResource(mContext, testSmsCbMessage);
143         assertEquals(expectedResult, result);
144 
145         SmsCbMessage testPublicSafetyMessage = new SmsCbMessage(1, 2, 0, new SmsCbLocation(),
146                 SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PUBLIC_SAFETY, "language", "body",
147                 SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, null, new SmsCbCmasInfo(0, 2, 3,
148                 SmsCbCmasInfo.CMAS_SEVERITY_EXTREME, SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE,
149                 SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY), 0, 1);
150         result = getDialogTitleResource(mContext, testPublicSafetyMessage);
151         assertNotEquals(expectedResult, result);
152 
153         // received other channel message, check the res id matching service category
154         final int[] expectedResources = {
155                 R.string.pws_other_message_identifiers, R.string.cmas_presidential_level_alert,
156                 R.string.cmas_severe_alert, R.string.cmas_amber_alert,
157                 R.string.cmas_required_monthly_test,
158                 R.string.cmas_exercise_alert, R.string.cmas_operator_defined_alert,
159                 R.string.state_local_test_alert};
160         putResources(R.array.emergency_alerts_channels_range_strings,
161                 new String[]{"0x1123:rat=gsm, emergency=true"});
162         putResources(R.array.cmas_presidential_alerts_channels_range_strings,
163                 new String[]{"0x1112:rat=gsm, emergency=true"});
164         putResources(R.array.cmas_alerts_severe_range_strings,
165                 new String[]{"0x1115:rat=gsm, emergency=true"});
166         putResources(R.array.cmas_amber_alerts_channels_range_strings,
167                 new String[]{"0x111B:rat=gsm, emergency=true"});
168         putResources(R.array.required_monthly_test_range_strings,
169                 new String[]{"0x111C:rat=gsm, emergency=true"});
170         putResources(R.array.exercise_alert_range_strings,
171                 new String[]{"0x111D:rat=gsm, emergency=true"});
172         putResources(R.array.operator_defined_alert_range_strings,
173                 new String[]{"0x111E:rat=gsm, emergency=true"});
174         putResources(R.array.state_local_test_alert_range_strings,
175                 new String[]{"0x1122:rat=gsm, emergency=true"});
176 
177         final int[] serviceCategory =
178                 {0x1123, 0x1112, 0x1115, 0x111B, 0x111C, 0x111D, 0x111E, 0x1122};
179         for (int i = 0; i < serviceCategory.length; i++) {
180             SmsCbMessage message = new SmsCbMessage(0, 0, 0, null,
181                     serviceCategory[i], "", "", 0, null,
182                     null, 0, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
183             assertEquals(expectedResources[i], getDialogTitleResource(mContext, message));
184         }
185     }
186 
187     @Test
testGetCmasResponseResId()188     public void testGetCmasResponseResId() throws Exception {
189         int[] resps = {SmsCbCmasInfo.CMAS_RESPONSE_TYPE_SHELTER,
190                 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_EVACUATE,
191                 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_PREPARE,
192                 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_EXECUTE,
193                 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR,
194                 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_AVOID,
195                 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_ASSESS,
196                 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_NONE};
197         for (int r : resps) {
198             assertNotEquals(0, getCmasResponseResId(new SmsCbCmasInfo(0, 0, r, 0, 0, 0)));
199         }
200 
201         assertEquals(0, getCmasResponseResId(new SmsCbCmasInfo(
202                 0, 0, SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, 0, 0, 0)));
203     }
204 
205     @Test
testGetSmsSenderAddressResource()206     public void testGetSmsSenderAddressResource() throws Exception {
207         mockSubscriptionManager();
208         mockTelephonyManager();
209         setFakeSharedPreferences();
210         doReturn(new Configuration()).when(mResources).getConfiguration();
211         doReturn(mContext).when(mContext).createConfigurationContext(any());
212 
213         final int[] expectedResources = {
214                 R.string.sms_cb_sender_name_presidential, R.string.sms_cb_sender_name_emergency,
215                 R.string.sms_cb_sender_name_public_safety, R.string.sms_cb_sender_name_default};
216         putResources(R.array.cmas_presidential_alerts_channels_range_strings,
217                 new String[]{"0x1112:rat=gsm, emergency=true"});
218         putResources(R.array.emergency_alerts_channels_range_strings,
219                 new String[]{"0x111B:rat=gsm, emergency=true"});
220         putResources(R.array.public_safety_messages_channels_range_strings,
221                 new String[]{"0x112C:rat=gsm, emergency=true"});
222         putResources(R.array.cmas_alert_extreme_channels_range_strings,
223                 new String[]{"0x1113:rat=gsm, emergency=true"});
224 
225         final String[] expectedStrings = {
226                 "Wireless emergency alerts(presidential)", "Wireless emergency alerts(emergency)",
227                 "Informational notification", "Wireless emergency alerts(default)"};
228         doReturn(expectedStrings[0]).when(mResources).getText(eq(expectedResources[0]));
229         doReturn(expectedStrings[1]).when(mResources).getText(eq(expectedResources[1]));
230         doReturn(expectedStrings[2]).when(mResources).getText(eq(expectedResources[2]));
231         doReturn(expectedStrings[3]).when(mResources).getText(eq(expectedResources[3]));
232 
233         // check the sms sender address resource id and string
234         final int[] serviceCategory = {0x1112, 0x111B, 0x112C, 0x1113};
235         for (int i = 0; i < serviceCategory.length; i++) {
236             SmsCbMessage message = new SmsCbMessage(0, 0, 0, null,
237                     serviceCategory[i], "", "", 0, null,
238                     null, 0, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
239             assertEquals(expectedResources[i],
240                     CellBroadcastResources.getSmsSenderAddressResource(mContext, message));
241             assertEquals(expectedStrings[i],
242                     CellBroadcastResources.getSmsSenderAddressResourceEnglishString(mContext,
243                             message));
244         }
245     }
246 
247     @Test
testGetDialogPictogramResource()248     public void testGetDialogPictogramResource() throws Exception {
249         final int[] expectedResources = {R.drawable.pict_icon_earthquake,
250                 R.drawable.pict_icon_earthquake, R.drawable.pict_icon_tsunami};
251         final int[] warningType = {SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE,
252                 SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI,
253                 SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI};
254 
255         // when SmsCbEtwsInfo exist, check the drawable res id that matches the warningType
256         for (int i = 0; i < warningType.length; i++) {
257             SmsCbMessage message = new SmsCbMessage(0, 0, 0, null, 0, "", "", 0,
258                     new SmsCbEtwsInfo(warningType[i], false, false, false, null),
259                     null, 0, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
260             assertEquals(expectedResources[i], getDialogPictogramResource(mContext, message));
261         }
262 
263         mockTelephonyManager();
264         setFakeSharedPreferences();
265         putResources(R.array.additional_cbs_channels_strings,
266                 new String[]{"0xA800:type=etws_earthquake, emergency=true",
267                         "0xAFEE:type=etws_tsunami, emergency=true",
268                         "0xAC00:type=other, emergency=true",
269                         "0xA802:type=test, emergency=false"});
270         // received an additional channel message, check the drawable res id matching alertType
271         final int[] expectedResources2 = {R.drawable.pict_icon_earthquake,
272                 R.drawable.pict_icon_tsunami, -1, -1};
273         final int[] serviceCategory = {0xA800, 0xAFEE, 0xAC00, 0xA802};
274         for (int i = 0; i < serviceCategory.length; i++) {
275             SmsCbMessage message = new SmsCbMessage(0, 0, 0, null,
276                     serviceCategory[i], "", "", 0, null,
277                     null, 0, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
278             assertEquals(expectedResources2[i], getDialogPictogramResource(mContext, message));
279         }
280     }
281 
282     @Test
testOverrideTranslation()283     public void testOverrideTranslation() throws Exception {
284         final String expectedStrings = "Presidential alert";
285 
286         doReturn(expectedStrings).when(mResources)
287                 .getText(R.string.cmas_presidential_level_alert);
288         doReturn(false).when(mResources)
289                 .getBoolean(R.bool.override_alert_title_language_to_match_message_locale);
290 
291         Context mockContext2 = mock(Context.class);
292         doReturn(mResources).when(mockContext2).getResources();
293         Configuration config = new Configuration();
294         doReturn(config).when(mResources).getConfiguration();
295         doReturn(mockContext2).when(mContext).createConfigurationContext(any());
296 
297         String strResult = CellBroadcastResources.overrideTranslation(mContext,
298                 R.string.cmas_presidential_level_alert, mResources, "en");
299         verify(mContext, times(0)).createConfigurationContext(any());
300         assertEquals(expectedStrings, strResult);
301 
302         doReturn(true).when(mResources)
303                 .getBoolean(R.bool.override_alert_title_language_to_match_message_locale);
304 
305         CellBroadcastResources.overrideTranslation(mContext,
306                 R.string.cmas_presidential_level_alert, mResources, "en");
307 
308         verify(mContext, times(1)).createConfigurationContext(any());
309     }
310 
311     @Test
testGetDialogTitleResourceForExistEtwsWarningInfo()312     public void testGetDialogTitleResourceForExistEtwsWarningInfo() throws Exception {
313         setFakeSharedPreferences();
314 
315         // when SmsCbEtwsInfo exist, check the string res id that matches the warningType
316         final int[] expectedResources = {R.string.etws_earthquake_warning,
317                 R.string.etws_tsunami_warning, R.string.etws_earthquake_and_tsunami_warning,
318                 R.string.etws_test_message, R.string.etws_other_emergency_type,
319                 R.string.etws_other_emergency_type};
320         final int[] warningType = {SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE,
321                 SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI,
322                 SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI,
323                 SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE,
324                 SmsCbEtwsInfo.ETWS_WARNING_TYPE_OTHER_EMERGENCY,
325                 SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN};
326         for (int i = 0; i < warningType.length; i++) {
327             SmsCbMessage message = new SmsCbMessage(0, 0, 0, null, 0, "", "", 0,
328                     new SmsCbEtwsInfo(warningType[i], false, false, false, null),
329                     null, 0, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
330             assertEquals(expectedResources[i], getDialogTitleResource(mContext, message));
331         }
332     }
333 
334     @Test
testGetDialogTitleResourceForAdditionalChannel()335     public void testGetDialogTitleResourceForAdditionalChannel() throws Exception {
336         setFakeSharedPreferences();
337 
338         // received an additional channel message, check the res id matching alertType
339         CellBroadcastChannelManager.clearAllCellBroadcastChannelRanges();
340         mockTelephonyManager();
341         putResources(R.array.additional_cbs_channels_strings,
342                 new String[]{"0xAC01:type=default, emergency=true",
343                         "0xA800:type=etws_earthquake, emergency=true",
344                         "0xAFEE:type=etws_tsunami, emergency=true",
345                         "0xA802:type=test, emergency=true",
346                         "0xAC00:type=other, emergency=true",
347                         "0xA803:type=etws_default, emergency=true",
348                         "0xA804:type=mute, emergency=true",
349                         "0xA805:type=test, emergency=false"});
350         final int[] expectedResources = {R.string.pws_other_message_identifiers,
351                 R.string.etws_earthquake_warning, R.string.etws_tsunami_warning,
352                 R.string.etws_test_message,
353                 R.string.etws_other_emergency_type, R.string.etws_other_emergency_type,
354                 R.string.pws_other_message_identifiers, R.string.cb_other_message_identifiers};
355         final int[] serviceCategory =
356                 {0xAC01, 0xA800, 0xAFEE, 0xA802, 0xAC00, 0xA803, 0xA804, 0xA805};
357         for (int i = 0; i < serviceCategory.length; i++) {
358             SmsCbMessage message = new SmsCbMessage(0, 0, 0, null, serviceCategory[i], "", "", 0,
359                     null, null, 0, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
360             assertEquals(expectedResources[i], getDialogTitleResource(mContext, message));
361         }
362     }
363 
getCmasCategoryResId(SmsCbCmasInfo info)364     private int getCmasCategoryResId(SmsCbCmasInfo info) throws Exception {
365         Method method = CellBroadcastResources.class.getDeclaredMethod(
366                 "getCmasCategoryResId", SmsCbCmasInfo.class);
367         method.setAccessible(true);
368         return (int) method.invoke(null, info);
369     }
370 
getCmasResponseResId(SmsCbCmasInfo info)371     private int getCmasResponseResId(SmsCbCmasInfo info) throws Exception {
372         Method method = CellBroadcastResources.class.getDeclaredMethod(
373                 "getCmasResponseResId", SmsCbCmasInfo.class);
374         method.setAccessible(true);
375         return (int) method.invoke(null, info);
376     }
377 
getDialogTitleResource(Context context, SmsCbMessage info)378     private int getDialogTitleResource(Context context, SmsCbMessage info) throws Exception {
379         Method method = CellBroadcastResources.class.getDeclaredMethod(
380                 "getDialogTitleResource", Context.class, SmsCbMessage.class);
381         method.setAccessible(true);
382         return (int) method.invoke(null, context, info);
383     }
384 
getDialogPictogramResource(Context context, SmsCbMessage info)385     private int getDialogPictogramResource(Context context, SmsCbMessage info) throws Exception {
386         Method method = CellBroadcastResources.class.getDeclaredMethod(
387                 "getDialogPictogramResource", Context.class, SmsCbMessage.class);
388         method.setAccessible(true);
389         return (int) method.invoke(null, context, info);
390     }
391 
putResources(int id, String[] values)392     void putResources(int id, String[] values) {
393         doReturn(values).when(mResources).getStringArray(eq(id));
394     }
395 
setFakeSharedPreferences()396     private void setFakeSharedPreferences() {
397         FakeSharedPreferences mFakeSharedPreferences = new FakeSharedPreferences();
398         doReturn(mFakeSharedPreferences).when(mContext).getSharedPreferences(anyString(), anyInt());
399     }
400 
mockSubscriptionManager()401     private void mockSubscriptionManager() {
402         SubscriptionManager mockSubManager = mock(SubscriptionManager.class);
403         doReturn(mockSubManager).when(mContext).getSystemService(
404                 eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE));
405         SubscriptionInfo mockSubInfo = mock(SubscriptionInfo.class);
406         doReturn(mockSubInfo).when(mockSubManager).getActiveSubscriptionInfo(anyInt());
407     }
408 
mockTelephonyManager()409     private void mockTelephonyManager() {
410         TelephonyManager mMockTelephonyManager = mock(TelephonyManager.class);
411         doReturn(mMockTelephonyManager).when(mMockTelephonyManager)
412                 .createForSubscriptionId(anyInt());
413         doReturn(Context.TELEPHONY_SERVICE).when(mContext).getSystemServiceName(
414                 TelephonyManager.class);
415         doReturn(mMockTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
416     }
417 }
418