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