1 /* 2 * Copyright (C) 2023 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.phone.satellite.accesscontrol; 18 19 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED; 20 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_LOCATION_NOT_AVAILABLE; 21 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_ERROR; 22 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED; 23 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS; 24 25 import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.EVENT_CONFIG_DATA_UPDATED; 26 import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.GOOGLE_US_SAN_SAT_S2_FILE_NAME; 27 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.assertFalse; 30 import static org.junit.Assert.assertNull; 31 import static org.junit.Assert.assertSame; 32 import static org.junit.Assert.assertTrue; 33 import static org.junit.Assert.fail; 34 import static org.mockito.ArgumentMatchers.any; 35 import static org.mockito.ArgumentMatchers.anyBoolean; 36 import static org.mockito.ArgumentMatchers.anyInt; 37 import static org.mockito.ArgumentMatchers.anyString; 38 import static org.mockito.ArgumentMatchers.eq; 39 import static org.mockito.Mockito.clearInvocations; 40 import static org.mockito.Mockito.doAnswer; 41 import static org.mockito.Mockito.doReturn; 42 import static org.mockito.Mockito.mock; 43 import static org.mockito.Mockito.never; 44 import static org.mockito.Mockito.spy; 45 import static org.mockito.Mockito.times; 46 import static org.mockito.Mockito.verify; 47 import static org.mockito.Mockito.when; 48 49 import android.annotation.Nullable; 50 import android.content.Context; 51 import android.content.SharedPreferences; 52 import android.content.res.Resources; 53 import android.location.Location; 54 import android.location.LocationManager; 55 import android.location.LocationRequest; 56 import android.os.AsyncResult; 57 import android.os.Bundle; 58 import android.os.CancellationSignal; 59 import android.os.DropBoxManager; 60 import android.os.Handler; 61 import android.os.HandlerThread; 62 import android.os.Looper; 63 import android.os.Message; 64 import android.os.ResultReceiver; 65 import android.telecom.TelecomManager; 66 import android.telephony.satellite.SatelliteManager; 67 import android.testing.TestableLooper; 68 import android.util.Log; 69 import android.util.Pair; 70 71 import androidx.test.ext.junit.runners.AndroidJUnit4; 72 73 import com.android.internal.telephony.Phone; 74 import com.android.internal.telephony.PhoneFactory; 75 import com.android.internal.telephony.TelephonyCountryDetector; 76 import com.android.internal.telephony.flags.FeatureFlags; 77 import com.android.internal.telephony.satellite.SatelliteConfig; 78 import com.android.internal.telephony.satellite.SatelliteConfigParser; 79 import com.android.internal.telephony.satellite.SatelliteController; 80 import com.android.internal.telephony.satellite.SatelliteModemInterface; 81 82 import org.junit.After; 83 import org.junit.Before; 84 import org.junit.Test; 85 import org.junit.runner.RunWith; 86 import org.mockito.ArgumentCaptor; 87 import org.mockito.Captor; 88 import org.mockito.Mock; 89 import org.mockito.MockitoAnnotations; 90 91 import java.io.File; 92 import java.lang.reflect.Field; 93 import java.util.ArrayList; 94 import java.util.Arrays; 95 import java.util.HashMap; 96 import java.util.List; 97 import java.util.Map; 98 import java.util.Set; 99 import java.util.concurrent.Executor; 100 import java.util.concurrent.Semaphore; 101 import java.util.concurrent.TimeUnit; 102 import java.util.function.Consumer; 103 104 /** Unit test for {@link SatelliteAccessController} */ 105 @RunWith(AndroidJUnit4.class) 106 public class SatelliteAccessControllerTest { 107 private static final String TAG = "SatelliteAccessControllerTest"; 108 private static final String[] TEST_SATELLITE_COUNTRY_CODES = {"US", "CA", "UK"}; 109 private static final String TEST_SATELLITE_S2_FILE = "sat_s2_file.dat"; 110 private static final boolean TEST_SATELLITE_ALLOW = true; 111 private static final int TEST_LOCATION_FRESH_DURATION_SECONDS = 10; 112 private static final long TEST_LOCATION_FRESH_DURATION_NANOS = 113 TimeUnit.SECONDS.toNanos(TEST_LOCATION_FRESH_DURATION_SECONDS); 114 private static final long TIMEOUT = 500; 115 private static final List<String> EMPTY_STRING_LIST = new ArrayList<>(); 116 private static final List<String> LOCATION_PROVIDERS = 117 listOf(LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER); 118 private static final int SUB_ID = 0; 119 120 @Mock 121 private LocationManager mMockLocationManager; 122 @Mock 123 private TelecomManager mMockTelecomManager; 124 @Mock 125 private TelephonyCountryDetector mMockCountryDetector; 126 @Mock 127 private SatelliteController mMockSatelliteController; 128 @Mock 129 private SatelliteModemInterface mMockSatelliteModemInterface; 130 @Mock 131 private DropBoxManager mMockDropBoxManager; 132 @Mock 133 private Context mMockContext; 134 @Mock 135 private Phone mMockPhone; 136 @Mock 137 private Phone mMockPhone2; 138 @Mock 139 private FeatureFlags mMockFeatureFlags; 140 @Mock 141 private Resources mMockResources; 142 @Mock 143 private SatelliteOnDeviceAccessController mMockSatelliteOnDeviceAccessController; 144 @Mock 145 Location mMockLocation0; 146 @Mock 147 Location mMockLocation1; 148 @Mock 149 File mMockSatS2File; 150 @Mock 151 SharedPreferences mMockSharedPreferences; 152 @Mock 153 private SharedPreferences.Editor mMockSharedPreferencesEditor; 154 @Mock 155 private Map<SatelliteOnDeviceAccessController.LocationToken, Boolean> 156 mMockCachedAccessRestrictionMap; 157 158 private Looper mLooper; 159 private TestableLooper mTestableLooper; 160 private Phone[] mPhones; 161 private TestSatelliteAccessController mSatelliteAccessControllerUT; 162 163 @Captor 164 private ArgumentCaptor<CancellationSignal> mLocationRequestCancellationSignalCaptor; 165 @Captor 166 private ArgumentCaptor<Consumer<Location>> mLocationRequestConsumerCaptor; 167 @Captor 168 private ArgumentCaptor<Handler> mConfigUpdateHandlerCaptor; 169 @Captor 170 private ArgumentCaptor<Integer> mConfigUpdateIntCaptor; 171 @Captor 172 private ArgumentCaptor<Object> mConfigUpdateObjectCaptor; 173 private boolean mQueriedSatelliteAllowed = false; 174 private int mQueriedSatelliteAllowedResultCode = SATELLITE_RESULT_SUCCESS; 175 private Semaphore mSatelliteAllowedSemaphore = new Semaphore(0); 176 private ResultReceiver mSatelliteAllowedReceiver = new ResultReceiver(null) { 177 @Override 178 protected void onReceiveResult(int resultCode, Bundle resultData) { 179 mQueriedSatelliteAllowedResultCode = resultCode; 180 if (resultCode == SATELLITE_RESULT_SUCCESS) { 181 if (resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) { 182 mQueriedSatelliteAllowed = resultData.getBoolean( 183 KEY_SATELLITE_COMMUNICATION_ALLOWED); 184 } else { 185 logd("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist."); 186 mQueriedSatelliteAllowed = false; 187 } 188 } else { 189 logd("mSatelliteAllowedReceiver: resultCode=" + resultCode); 190 mQueriedSatelliteAllowed = false; 191 } 192 try { 193 mSatelliteAllowedSemaphore.release(); 194 } catch (Exception ex) { 195 fail("mSatelliteAllowedReceiver: Got exception in releasing semaphore, ex=" + ex); 196 } 197 } 198 }; 199 200 @Before setUp()201 public void setUp() throws Exception { 202 logd("setUp"); 203 MockitoAnnotations.initMocks(this); 204 205 if (Looper.myLooper() == null) { 206 Looper.prepare(); 207 } 208 209 HandlerThread handlerThread = new HandlerThread("SatelliteAccessControllerTest"); 210 handlerThread.start(); 211 mLooper = handlerThread.getLooper(); 212 mTestableLooper = new TestableLooper(mLooper); 213 when(mMockContext.getSystemServiceName(LocationManager.class)).thenReturn( 214 Context.LOCATION_SERVICE); 215 when(mMockContext.getSystemServiceName(TelecomManager.class)).thenReturn( 216 Context.TELECOM_SERVICE); 217 when(mMockContext.getSystemServiceName(DropBoxManager.class)).thenReturn( 218 Context.DROPBOX_SERVICE); 219 when(mMockContext.getSystemService(LocationManager.class)).thenReturn( 220 mMockLocationManager); 221 when(mMockContext.getSystemService(TelecomManager.class)).thenReturn( 222 mMockTelecomManager); 223 when(mMockContext.getSystemService(DropBoxManager.class)).thenReturn( 224 mMockDropBoxManager); 225 mPhones = new Phone[]{mMockPhone, mMockPhone2}; 226 replaceInstance(PhoneFactory.class, "sPhones", null, mPhones); 227 replaceInstance(SatelliteController.class, "sInstance", null, 228 mMockSatelliteController); 229 replaceInstance(SatelliteModemInterface.class, "sInstance", null, 230 mMockSatelliteModemInterface); 231 replaceInstance(TelephonyCountryDetector.class, "sInstance", null, 232 mMockCountryDetector); 233 when(mMockContext.getResources()).thenReturn(mMockResources); 234 when(mMockResources.getStringArray( 235 com.android.internal.R.array.config_oem_enabled_satellite_country_codes)) 236 .thenReturn(TEST_SATELLITE_COUNTRY_CODES); 237 when(mMockResources.getBoolean( 238 com.android.internal.R.bool.config_oem_enabled_satellite_access_allow)) 239 .thenReturn(TEST_SATELLITE_ALLOW); 240 when(mMockResources.getString( 241 com.android.internal.R.string.config_oem_enabled_satellite_s2cell_file)) 242 .thenReturn(TEST_SATELLITE_S2_FILE); 243 when(mMockResources.getInteger(com.android.internal.R.integer 244 .config_oem_enabled_satellite_location_fresh_duration)) 245 .thenReturn(TEST_LOCATION_FRESH_DURATION_SECONDS); 246 247 when(mMockLocationManager.getProviders(true)).thenReturn(LOCATION_PROVIDERS); 248 when(mMockLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)) 249 .thenReturn(mMockLocation0); 250 when(mMockLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)) 251 .thenReturn(mMockLocation1); 252 when(mMockLocation0.getLatitude()).thenReturn(0.0); 253 when(mMockLocation0.getLongitude()).thenReturn(0.0); 254 when(mMockLocation1.getLatitude()).thenReturn(1.0); 255 when(mMockLocation1.getLongitude()).thenReturn(1.0); 256 when(mMockSatelliteOnDeviceAccessController.isSatCommunicationAllowedAtLocation( 257 any(SatelliteOnDeviceAccessController.LocationToken.class))).thenReturn(true); 258 259 when(mMockContext.getSharedPreferences(anyString(), anyInt())).thenReturn( 260 mMockSharedPreferences); 261 when(mMockSharedPreferences.getBoolean(anyString(), anyBoolean())).thenReturn(true); 262 when(mMockSharedPreferences.getStringSet(anyString(), any())) 263 .thenReturn(Set.of(TEST_SATELLITE_COUNTRY_CODES)); 264 doReturn(mMockSharedPreferencesEditor).when(mMockSharedPreferences).edit(); 265 doReturn(mMockSharedPreferencesEditor).when(mMockSharedPreferencesEditor) 266 .putBoolean(anyString(), anyBoolean()); 267 doReturn(mMockSharedPreferencesEditor).when(mMockSharedPreferencesEditor) 268 .putStringSet(anyString(), any()); 269 270 when(mMockFeatureFlags.satellitePersistentLogging()).thenReturn(true); 271 272 mSatelliteAccessControllerUT = new TestSatelliteAccessController(mMockContext, 273 mMockFeatureFlags, mLooper, mMockLocationManager, mMockTelecomManager, 274 mMockSatelliteOnDeviceAccessController, mMockSatS2File); 275 mTestableLooper.processAllMessages(); 276 } 277 278 @After tearDown()279 public void tearDown() throws Exception { 280 logd("tearDown"); 281 if (mTestableLooper != null) { 282 mTestableLooper.destroy(); 283 mTestableLooper = null; 284 } 285 286 if (mLooper != null) { 287 mLooper.quit(); 288 mLooper = null; 289 } 290 } 291 292 @Test testGetInstance()293 public void testGetInstance() { 294 SatelliteAccessController inst1 = 295 SatelliteAccessController.getOrCreateInstance(mMockContext, mMockFeatureFlags); 296 SatelliteAccessController inst2 = 297 SatelliteAccessController.getOrCreateInstance(mMockContext, mMockFeatureFlags); 298 assertEquals(inst1, inst2); 299 } 300 301 @Test testRequestIsSatelliteCommunicationAllowedForCurrentLocation()302 public void testRequestIsSatelliteCommunicationAllowedForCurrentLocation() throws Exception { 303 // OEM-enabled satellite is not supported 304 when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false); 305 mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation( 306 SUB_ID, mSatelliteAllowedReceiver); 307 mTestableLooper.processAllMessages(); 308 assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult( 309 mSatelliteAllowedSemaphore, 1)); 310 assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, mQueriedSatelliteAllowedResultCode); 311 312 // OEM-enabled satellite is supported 313 when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true); 314 315 // Satellite is not supported 316 setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS); 317 clearAllInvocations(); 318 mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation( 319 SUB_ID, mSatelliteAllowedReceiver); 320 mTestableLooper.processAllMessages(); 321 assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult( 322 mSatelliteAllowedSemaphore, 1)); 323 assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode); 324 assertFalse(mQueriedSatelliteAllowed); 325 326 // Failed to query whether satellite is supported or not 327 setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_MODEM_ERROR); 328 clearAllInvocations(); 329 mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation( 330 SUB_ID, mSatelliteAllowedReceiver); 331 mTestableLooper.processAllMessages(); 332 assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult( 333 mSatelliteAllowedSemaphore, 1)); 334 assertEquals(SATELLITE_RESULT_MODEM_ERROR, mQueriedSatelliteAllowedResultCode); 335 336 // Network country codes are not available. TelecomManager.isInEmergencyCall() returns true. 337 // On-device access controller will be used. Last known location is available and fresh. 338 clearAllInvocations(); 339 setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS); 340 setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS); 341 when(mMockCountryDetector.getCurrentNetworkCountryIso()).thenReturn(EMPTY_STRING_LIST); 342 when(mMockTelecomManager.isInEmergencyCall()).thenReturn(true); 343 mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1; 344 when(mMockLocation0.getElapsedRealtimeNanos()).thenReturn(2L); 345 when(mMockLocation1.getElapsedRealtimeNanos()).thenReturn(0L); 346 mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation( 347 SUB_ID, mSatelliteAllowedReceiver); 348 mTestableLooper.processAllMessages(); 349 assertTrue( 350 mSatelliteAccessControllerUT.isKeepOnDeviceAccessControllerResourcesTimerStarted()); 351 verify(mMockSatelliteOnDeviceAccessController).isSatCommunicationAllowedAtLocation( 352 any(SatelliteOnDeviceAccessController.LocationToken.class)); 353 assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult( 354 mSatelliteAllowedSemaphore, 1)); 355 assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode); 356 assertTrue(mQueriedSatelliteAllowed); 357 358 // Move time forward and verify resources are cleaned up 359 clearAllInvocations(); 360 mTestableLooper.moveTimeForward(mSatelliteAccessControllerUT 361 .getKeepOnDeviceAccessControllerResourcesTimeoutMillis()); 362 mTestableLooper.processAllMessages(); 363 assertFalse( 364 mSatelliteAccessControllerUT.isKeepOnDeviceAccessControllerResourcesTimerStarted()); 365 assertTrue(mSatelliteAccessControllerUT.isSatelliteOnDeviceAccessControllerReset()); 366 verify(mMockSatelliteOnDeviceAccessController).close(); 367 368 // Restore SatelliteOnDeviceAccessController for next verification 369 mSatelliteAccessControllerUT.setSatelliteOnDeviceAccessController( 370 mMockSatelliteOnDeviceAccessController); 371 372 // Network country codes are not available. TelecomManager.isInEmergencyCall() returns 373 // false. Phone0 is in ECM. On-device access controller will be used. Last known location is 374 // not fresh. 375 clearAllInvocations(); 376 when(mMockCountryDetector.getCurrentNetworkCountryIso()).thenReturn(EMPTY_STRING_LIST); 377 when(mMockTelecomManager.isInEmergencyCall()).thenReturn(false); 378 when(mMockPhone.isInEcm()).thenReturn(true); 379 mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1; 380 when(mMockLocation0.getElapsedRealtimeNanos()).thenReturn(0L); 381 when(mMockLocation1.getElapsedRealtimeNanos()).thenReturn(0L); 382 mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation( 383 SUB_ID, mSatelliteAllowedReceiver); 384 mTestableLooper.processAllMessages(); 385 assertFalse( 386 mSatelliteAccessControllerUT.isKeepOnDeviceAccessControllerResourcesTimerStarted()); 387 verify(mMockLocationManager).getCurrentLocation(eq(LocationManager.GPS_PROVIDER), 388 any(LocationRequest.class), mLocationRequestCancellationSignalCaptor.capture(), 389 any(Executor.class), mLocationRequestConsumerCaptor.capture()); 390 assertTrue(mSatelliteAccessControllerUT.isWaitForCurrentLocationTimerStarted()); 391 sendLocationRequestResult(mMockLocation0); 392 assertFalse(mSatelliteAccessControllerUT.isWaitForCurrentLocationTimerStarted()); 393 // The LocationToken should be already in the cache 394 verify(mMockSatelliteOnDeviceAccessController, never()).isSatCommunicationAllowedAtLocation( 395 any(SatelliteOnDeviceAccessController.LocationToken.class)); 396 assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult( 397 mSatelliteAllowedSemaphore, 1)); 398 assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode); 399 assertTrue(mQueriedSatelliteAllowed); 400 401 // Timed out to wait for current location. No cached allowed state. 402 clearAllInvocations(); 403 mSatelliteAccessControllerUT.setIsSatelliteCommunicationAllowedForCurrentLocationCache( 404 "cache_clear_and_not_allowed"); 405 when(mMockCountryDetector.getCurrentNetworkCountryIso()).thenReturn(EMPTY_STRING_LIST); 406 when(mMockTelecomManager.isInEmergencyCall()).thenReturn(false); 407 when(mMockPhone.isInEcm()).thenReturn(true); 408 mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1; 409 when(mMockLocation0.getElapsedRealtimeNanos()).thenReturn(0L); 410 when(mMockLocation1.getElapsedRealtimeNanos()).thenReturn(0L); 411 when(mMockCountryDetector.getCachedLocationCountryIsoInfo()).thenReturn(new Pair<>("", 0L)); 412 when(mMockCountryDetector.getCachedNetworkCountryIsoInfo()).thenReturn(new HashMap<>()); 413 mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation( 414 SUB_ID, mSatelliteAllowedReceiver); 415 mTestableLooper.processAllMessages(); 416 assertFalse( 417 mSatelliteAccessControllerUT.isKeepOnDeviceAccessControllerResourcesTimerStarted()); 418 verify(mMockLocationManager).getCurrentLocation(anyString(), any(LocationRequest.class), 419 any(CancellationSignal.class), any(Executor.class), any(Consumer.class)); 420 assertTrue(mSatelliteAccessControllerUT.isWaitForCurrentLocationTimerStarted()); 421 // Timed out 422 mTestableLooper.moveTimeForward( 423 mSatelliteAccessControllerUT.getWaitForCurrentLocationTimeoutMillis()); 424 mTestableLooper.processAllMessages(); 425 assertFalse(mSatelliteAccessControllerUT.isWaitForCurrentLocationTimerStarted()); 426 verify(mMockSatelliteOnDeviceAccessController, never()).isSatCommunicationAllowedAtLocation( 427 any(SatelliteOnDeviceAccessController.LocationToken.class)); 428 assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult( 429 mSatelliteAllowedSemaphore, 1)); 430 assertEquals(SATELLITE_RESULT_LOCATION_NOT_AVAILABLE, mQueriedSatelliteAllowedResultCode); 431 432 // Network country codes are not available. TelecomManager.isInEmergencyCall() returns 433 // false. No phone is in ECM. Last known location is not fresh. Cached country codes should 434 // be used for verifying satellite allow. No cached country codes are available. 435 clearAllInvocations(); 436 when(mMockCountryDetector.getCurrentNetworkCountryIso()).thenReturn(EMPTY_STRING_LIST); 437 when(mMockCountryDetector.getCachedLocationCountryIsoInfo()).thenReturn(new Pair<>("", 0L)); 438 when(mMockCountryDetector.getCachedNetworkCountryIsoInfo()).thenReturn(new HashMap<>()); 439 when(mMockTelecomManager.isInEmergencyCall()).thenReturn(false); 440 when(mMockPhone.isInEcm()).thenReturn(false); 441 when(mMockPhone2.isInEcm()).thenReturn(false); 442 mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1; 443 when(mMockLocation0.getElapsedRealtimeNanos()).thenReturn(0L); 444 when(mMockLocation1.getElapsedRealtimeNanos()).thenReturn(0L); 445 mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation( 446 SUB_ID, mSatelliteAllowedReceiver); 447 mTestableLooper.processAllMessages(); 448 verify(mMockLocationManager, never()).getCurrentLocation(anyString(), 449 any(LocationRequest.class), any(CancellationSignal.class), any(Executor.class), 450 any(Consumer.class)); 451 verify(mMockSatelliteOnDeviceAccessController, never()).isSatCommunicationAllowedAtLocation( 452 any(SatelliteOnDeviceAccessController.LocationToken.class)); 453 assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult( 454 mSatelliteAllowedSemaphore, 1)); 455 assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode); 456 assertFalse(mQueriedSatelliteAllowed); 457 } 458 459 @Test testUpdateSatelliteConfigData()460 public void testUpdateSatelliteConfigData() throws Exception { 461 verify(mMockSatelliteController).registerForConfigUpdateChanged( 462 mConfigUpdateHandlerCaptor.capture(), mConfigUpdateIntCaptor.capture(), 463 mConfigUpdateObjectCaptor.capture()); 464 465 assertSame(mConfigUpdateHandlerCaptor.getValue(), mSatelliteAccessControllerUT); 466 assertSame(mConfigUpdateIntCaptor.getValue(), EVENT_CONFIG_DATA_UPDATED); 467 assertSame(mConfigUpdateObjectCaptor.getValue(), mMockContext); 468 469 replaceInstance(SatelliteAccessController.class, "mCachedAccessRestrictionMap", 470 mSatelliteAccessControllerUT, mMockCachedAccessRestrictionMap); 471 472 // These APIs are executed during loadRemoteConfigs 473 verify(mMockSharedPreferences, times(1)).getStringSet(anyString(), any()); 474 verify(mMockSharedPreferences, times(1)).getBoolean(anyString(), anyBoolean()); 475 476 // satelliteConfig is null 477 SatelliteConfigParser spyConfigParser = 478 spy(new SatelliteConfigParser("test".getBytes())); 479 doReturn(spyConfigParser).when(mMockSatelliteController).getSatelliteConfigParser(); 480 assertNull(spyConfigParser.getConfig()); 481 482 sendConfigUpdateChangedEvent(mMockContext); 483 verify(mMockSharedPreferences, never()).edit(); 484 verify(mMockCachedAccessRestrictionMap, never()).clear(); 485 486 // satelliteConfig has invalid country codes 487 SatelliteConfig mockConfig = mock(SatelliteConfig.class); 488 doReturn(List.of("USA", "JAP")).when(mockConfig).getDeviceSatelliteCountryCodes(); 489 doReturn(mockConfig).when(mMockSatelliteController).getSatelliteConfig(); 490 doReturn(false).when(mockConfig).isSatelliteDataForAllowedRegion(); 491 492 sendConfigUpdateChangedEvent(mMockContext); 493 verify(mMockSharedPreferences, never()).edit(); 494 verify(mMockCachedAccessRestrictionMap, never()).clear(); 495 496 // satelliteConfig does not have is_allow_access_control data 497 doReturn(List.of(TEST_SATELLITE_COUNTRY_CODES)) 498 .when(mockConfig).getDeviceSatelliteCountryCodes(); 499 doReturn(null).when(mockConfig).isSatelliteDataForAllowedRegion(); 500 501 sendConfigUpdateChangedEvent(mMockContext); 502 verify(mMockSharedPreferences, never()).edit(); 503 verify(mMockCachedAccessRestrictionMap, never()).clear(); 504 505 // satelliteConfig doesn't have S2CellFile 506 File mockFile = mock(File.class); 507 doReturn(false).when(mockFile).exists(); 508 doReturn(List.of(TEST_SATELLITE_COUNTRY_CODES)) 509 .when(mockConfig).getDeviceSatelliteCountryCodes(); 510 doReturn(true).when(mockConfig).isSatelliteDataForAllowedRegion(); 511 doReturn(mockFile).when(mockConfig).getSatelliteS2CellFile(mMockContext); 512 513 sendConfigUpdateChangedEvent(mMockContext); 514 verify(mMockSharedPreferences, never()).edit(); 515 verify(mMockCachedAccessRestrictionMap, never()).clear(); 516 517 // satelliteConfig has valid data 518 doReturn(mockConfig).when(mMockSatelliteController).getSatelliteConfig(); 519 File testS2File = mSatelliteAccessControllerUT 520 .getTestSatelliteS2File(GOOGLE_US_SAN_SAT_S2_FILE_NAME); 521 doReturn(List.of(TEST_SATELLITE_COUNTRY_CODES)) 522 .when(mockConfig).getDeviceSatelliteCountryCodes(); 523 doReturn(true).when(mockConfig).isSatelliteDataForAllowedRegion(); 524 doReturn(testS2File).when(mockConfig).getSatelliteS2CellFile(mMockContext); 525 526 sendConfigUpdateChangedEvent(mMockContext); 527 verify(mMockSharedPreferences, times(2)).edit(); 528 verify(mMockCachedAccessRestrictionMap, times(1)).clear(); 529 } 530 sendConfigUpdateChangedEvent(Context context)531 private void sendConfigUpdateChangedEvent(Context context) { 532 Message msg = mSatelliteAccessControllerUT.obtainMessage(EVENT_CONFIG_DATA_UPDATED); 533 msg.obj = new AsyncResult(context, SATELLITE_RESULT_SUCCESS, null); 534 msg.sendToTarget(); 535 mTestableLooper.processAllMessages(); 536 } 537 clearAllInvocations()538 private void clearAllInvocations() { 539 clearInvocations(mMockSatelliteController); 540 clearInvocations(mMockSatelliteOnDeviceAccessController); 541 clearInvocations(mMockLocationManager); 542 clearInvocations(mMockCountryDetector); 543 } 544 verifyCountryDetectorApisCalled()545 private void verifyCountryDetectorApisCalled() { 546 verify(mMockCountryDetector).getCurrentNetworkCountryIso(); 547 verify(mMockCountryDetector).getCachedLocationCountryIsoInfo(); 548 verify(mMockCountryDetector).getCachedLocationCountryIsoInfo(); 549 } 550 waitForRequestIsSatelliteAllowedForCurrentLocationResult(Semaphore semaphore, int expectedNumberOfEvents)551 private boolean waitForRequestIsSatelliteAllowedForCurrentLocationResult(Semaphore semaphore, 552 int expectedNumberOfEvents) { 553 for (int i = 0; i < expectedNumberOfEvents; i++) { 554 try { 555 if (!semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) { 556 logd("Timeout to receive " 557 + "requestIsCommunicationAllowedForCurrentLocation()" 558 + " callback"); 559 return false; 560 } 561 } catch (Exception ex) { 562 logd("waitForRequestIsSatelliteSupportedResult: Got exception=" + ex); 563 return false; 564 } 565 } 566 return true; 567 } 568 sendLocationRequestResult(Location location)569 private void sendLocationRequestResult(Location location) { 570 mLocationRequestConsumerCaptor.getValue().accept(location); 571 mTestableLooper.processAllMessages(); 572 } 573 setUpResponseForRequestIsSatelliteSupported( boolean isSatelliteSupported, @SatelliteManager.SatelliteResult int error)574 private void setUpResponseForRequestIsSatelliteSupported( 575 boolean isSatelliteSupported, @SatelliteManager.SatelliteResult int error) { 576 doAnswer(invocation -> { 577 ResultReceiver resultReceiver = invocation.getArgument(1); 578 if (error == SATELLITE_RESULT_SUCCESS) { 579 Bundle bundle = new Bundle(); 580 bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, isSatelliteSupported); 581 resultReceiver.send(error, bundle); 582 } else { 583 resultReceiver.send(error, Bundle.EMPTY); 584 } 585 return null; 586 }).when(mMockSatelliteController).requestIsSatelliteSupported(anyInt(), 587 any(ResultReceiver.class)); 588 } 589 setUpResponseForRequestIsSatelliteProvisioned( boolean isSatelliteProvisioned, @SatelliteManager.SatelliteResult int error)590 private void setUpResponseForRequestIsSatelliteProvisioned( 591 boolean isSatelliteProvisioned, @SatelliteManager.SatelliteResult int error) { 592 doAnswer(invocation -> { 593 ResultReceiver resultReceiver = invocation.getArgument(1); 594 if (error == SATELLITE_RESULT_SUCCESS) { 595 Bundle bundle = new Bundle(); 596 bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED, 597 isSatelliteProvisioned); 598 resultReceiver.send(error, bundle); 599 } else { 600 resultReceiver.send(error, Bundle.EMPTY); 601 } 602 return null; 603 }).when(mMockSatelliteController).requestIsSatelliteProvisioned(anyInt(), 604 any(ResultReceiver.class)); 605 } 606 607 @SafeVarargs listOf(E... values)608 private static <E> List<E> listOf(E... values) { 609 return Arrays.asList(values); 610 } 611 logd(String message)612 private static void logd(String message) { 613 Log.d(TAG, message); 614 } 615 replaceInstance(final Class c, final String instanceName, final Object obj, final Object newValue)616 private static void replaceInstance(final Class c, 617 final String instanceName, final Object obj, final Object newValue) throws Exception { 618 Field field = c.getDeclaredField(instanceName); 619 field.setAccessible(true); 620 field.set(obj, newValue); 621 } 622 623 private static class TestSatelliteAccessController extends SatelliteAccessController { 624 public long elapsedRealtimeNanos = 0; 625 626 /** 627 * Create a SatelliteAccessController instance. 628 * 629 * @param context The context associated with the 630 * {@link SatelliteAccessController} instance. 631 * @param featureFlags The FeatureFlags that are supported. 632 * @param looper The Looper to run the SatelliteAccessController 633 * on. 634 * @param locationManager The LocationManager for querying current 635 * location of the 636 * device. 637 * @param satelliteOnDeviceAccessController The on-device satellite access controller 638 * instance. 639 */ TestSatelliteAccessController(Context context, FeatureFlags featureFlags, Looper looper, LocationManager locationManager, TelecomManager telecomManager, SatelliteOnDeviceAccessController satelliteOnDeviceAccessController, File s2CellFile)640 protected TestSatelliteAccessController(Context context, FeatureFlags featureFlags, 641 Looper looper, LocationManager locationManager, TelecomManager telecomManager, 642 SatelliteOnDeviceAccessController satelliteOnDeviceAccessController, 643 File s2CellFile) { 644 super(context, featureFlags, looper, locationManager, telecomManager, 645 satelliteOnDeviceAccessController, s2CellFile); 646 } 647 648 @Override getElapsedRealtimeNanos()649 protected long getElapsedRealtimeNanos() { 650 return elapsedRealtimeNanos; 651 } 652 isKeepOnDeviceAccessControllerResourcesTimerStarted()653 public boolean isKeepOnDeviceAccessControllerResourcesTimerStarted() { 654 return hasMessages(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT); 655 } 656 isSatelliteOnDeviceAccessControllerReset()657 public boolean isSatelliteOnDeviceAccessControllerReset() { 658 synchronized (mLock) { 659 return (mSatelliteOnDeviceAccessController == null); 660 } 661 } 662 setSatelliteOnDeviceAccessController( @ullable SatelliteOnDeviceAccessController accessController)663 public void setSatelliteOnDeviceAccessController( 664 @Nullable SatelliteOnDeviceAccessController accessController) { 665 synchronized (mLock) { 666 mSatelliteOnDeviceAccessController = accessController; 667 } 668 } 669 getKeepOnDeviceAccessControllerResourcesTimeoutMillis()670 public long getKeepOnDeviceAccessControllerResourcesTimeoutMillis() { 671 return KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT_MILLIS; 672 } 673 getWaitForCurrentLocationTimeoutMillis()674 public long getWaitForCurrentLocationTimeoutMillis() { 675 return WAIT_FOR_CURRENT_LOCATION_TIMEOUT_MILLIS; 676 } 677 isWaitForCurrentLocationTimerStarted()678 public boolean isWaitForCurrentLocationTimerStarted() { 679 return hasMessages(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT); 680 } 681 } 682 } 683