1 /* <lambda>null2 * Copyright (C) 2022 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.systemui.statusbar.pipeline.mobile.data.repository.prod 18 19 import android.annotation.SuppressLint 20 import android.content.Intent 21 import android.net.ConnectivityManager 22 import android.net.Network 23 import android.net.NetworkCapabilities 24 import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED 25 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR 26 import android.net.NetworkCapabilities.TRANSPORT_ETHERNET 27 import android.net.NetworkCapabilities.TRANSPORT_WIFI 28 import android.net.vcn.VcnTransportInfo 29 import android.net.wifi.WifiInfo 30 import android.net.wifi.WifiManager 31 import android.os.Bundle 32 import android.os.ParcelUuid 33 import android.telephony.CarrierConfigManager 34 import android.telephony.ServiceState 35 import android.telephony.SubscriptionInfo 36 import android.telephony.SubscriptionManager 37 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID 38 import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET 39 import android.telephony.TelephonyCallback 40 import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener 41 import android.telephony.TelephonyManager 42 import android.testing.TestableLooper 43 import androidx.test.filters.SmallTest 44 import com.android.internal.telephony.PhoneConstants 45 import com.android.keyguard.KeyguardUpdateMonitor 46 import com.android.keyguard.KeyguardUpdateMonitorCallback 47 import com.android.settingslib.R 48 import com.android.settingslib.mobile.MobileMappings 49 import com.android.systemui.SysuiTestCase 50 import com.android.systemui.coroutines.collectLastValue 51 import com.android.systemui.flags.FakeFeatureFlagsClassic 52 import com.android.systemui.flags.Flags 53 import com.android.systemui.log.LogBuffer 54 import com.android.systemui.log.table.TableLogBuffer 55 import com.android.systemui.log.table.TableLogBufferFactory 56 import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory 57 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository 58 import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger 59 import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel 60 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel 61 import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository 62 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository 63 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName 64 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy 65 import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManagerProxy 66 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots 67 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository 68 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl 69 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository 70 import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl 71 import com.android.systemui.util.concurrency.FakeExecutor 72 import com.android.systemui.util.mockito.any 73 import com.android.systemui.util.mockito.argumentCaptor 74 import com.android.systemui.util.mockito.capture 75 import com.android.systemui.util.mockito.eq 76 import com.android.systemui.util.mockito.mock 77 import com.android.systemui.util.mockito.whenever 78 import com.android.systemui.util.time.FakeSystemClock 79 import com.android.wifitrackerlib.MergedCarrierEntry 80 import com.android.wifitrackerlib.WifiEntry 81 import com.android.wifitrackerlib.WifiPickerTracker 82 import com.google.common.truth.Truth.assertThat 83 import java.util.UUID 84 import kotlinx.coroutines.ExperimentalCoroutinesApi 85 import kotlinx.coroutines.flow.filterNotNull 86 import kotlinx.coroutines.flow.launchIn 87 import kotlinx.coroutines.flow.onEach 88 import kotlinx.coroutines.test.StandardTestDispatcher 89 import kotlinx.coroutines.test.TestScope 90 import kotlinx.coroutines.test.runCurrent 91 import kotlinx.coroutines.test.runTest 92 import org.junit.Assert.assertTrue 93 import org.junit.Before 94 import org.junit.Ignore 95 import org.junit.Test 96 import org.mockito.ArgumentMatchers.anyInt 97 import org.mockito.ArgumentMatchers.anyString 98 import org.mockito.Mock 99 import org.mockito.Mockito.verify 100 import org.mockito.MockitoAnnotations 101 102 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") 103 @OptIn(ExperimentalCoroutinesApi::class) 104 @SmallTest 105 // This is required because our [SubscriptionManager.OnSubscriptionsChangedListener] uses a looper 106 // to run the callback and this makes the looper place nicely with TestScope etc. 107 @TestableLooper.RunWithLooper 108 class MobileConnectionsRepositoryTest : SysuiTestCase() { 109 110 private val flags = 111 FakeFeatureFlagsClassic().also { 112 it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) 113 it.set(Flags.INSTANT_TETHER, true) 114 it.set(Flags.WIFI_SECONDARY_NETWORKS, true) 115 } 116 117 private lateinit var connectionFactory: MobileConnectionRepositoryImpl.Factory 118 private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory 119 private lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory 120 private lateinit var connectivityRepository: ConnectivityRepository 121 private lateinit var airplaneModeRepository: FakeAirplaneModeRepository 122 private lateinit var wifiRepository: WifiRepository 123 private lateinit var carrierConfigRepository: CarrierConfigRepository 124 125 @Mock private lateinit var connectivityManager: ConnectivityManager 126 @Mock private lateinit var subscriptionManager: SubscriptionManager 127 @Mock private lateinit var telephonyManager: TelephonyManager 128 @Mock private lateinit var logger: MobileInputLogger 129 @Mock private lateinit var summaryLogger: TableLogBuffer 130 @Mock private lateinit var logBufferFactory: TableLogBufferFactory 131 @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor 132 @Mock private lateinit var wifiManager: WifiManager 133 @Mock private lateinit var wifiPickerTrackerFactory: WifiPickerTrackerFactory 134 @Mock private lateinit var wifiPickerTracker: WifiPickerTracker 135 @Mock private lateinit var wifiTableLogBuffer: TableLogBuffer 136 137 private val mobileMappings = FakeMobileMappingsProxy() 138 private val subscriptionManagerProxy = FakeSubscriptionManagerProxy() 139 private val mainExecutor = FakeExecutor(FakeSystemClock()) 140 private val wifiLogBuffer = LogBuffer("wifi", maxSize = 100, logcatEchoTracker = mock()) 141 private val wifiPickerTrackerCallback = 142 argumentCaptor<WifiPickerTracker.WifiPickerTrackerCallback>() 143 144 private val testDispatcher = StandardTestDispatcher() 145 private val testScope = TestScope(testDispatcher) 146 147 private lateinit var underTest: MobileConnectionsRepositoryImpl 148 149 @Before 150 fun setUp() { 151 MockitoAnnotations.initMocks(this) 152 whenever(telephonyManager.simOperatorName).thenReturn("") 153 154 // Set up so the individual connection repositories 155 whenever(telephonyManager.createForSubscriptionId(anyInt())).thenAnswer { invocation -> 156 telephonyManager.also { 157 whenever(it.subscriptionId).thenReturn(invocation.getArgument(0)) 158 } 159 } 160 161 whenever(logBufferFactory.getOrCreate(anyString(), anyInt())).thenAnswer { _ -> 162 mock<TableLogBuffer>() 163 } 164 165 whenever(wifiPickerTrackerFactory.create(any(), capture(wifiPickerTrackerCallback), any())) 166 .thenReturn(wifiPickerTracker) 167 168 // For convenience, set up the subscription info callbacks 169 whenever(subscriptionManager.getActiveSubscriptionInfo(anyInt())).thenAnswer { invocation -> 170 when (invocation.getArgument(0) as Int) { 171 1 -> SUB_1 172 2 -> SUB_2 173 3 -> SUB_3 174 4 -> SUB_4 175 else -> null 176 } 177 } 178 179 connectivityRepository = 180 ConnectivityRepositoryImpl( 181 connectivityManager, 182 ConnectivitySlots(context), 183 context, 184 mock(), 185 mock(), 186 testScope.backgroundScope, 187 mock(), 188 ) 189 190 airplaneModeRepository = FakeAirplaneModeRepository() 191 192 wifiRepository = 193 WifiRepositoryImpl( 194 flags, 195 testScope.backgroundScope, 196 mainExecutor, 197 testDispatcher, 198 wifiPickerTrackerFactory, 199 wifiManager, 200 wifiLogBuffer, 201 wifiTableLogBuffer, 202 ) 203 204 carrierConfigRepository = 205 CarrierConfigRepository( 206 fakeBroadcastDispatcher, 207 mock(), 208 mock(), 209 logger, 210 testScope.backgroundScope, 211 ) 212 213 connectionFactory = 214 MobileConnectionRepositoryImpl.Factory( 215 context, 216 fakeBroadcastDispatcher, 217 connectivityManager, 218 telephonyManager = telephonyManager, 219 bgDispatcher = testDispatcher, 220 logger = logger, 221 mobileMappingsProxy = mobileMappings, 222 scope = testScope.backgroundScope, 223 flags = flags, 224 carrierConfigRepository = carrierConfigRepository, 225 ) 226 carrierMergedFactory = 227 CarrierMergedConnectionRepository.Factory( 228 telephonyManager, 229 testScope.backgroundScope.coroutineContext, 230 testScope.backgroundScope, 231 wifiRepository, 232 ) 233 fullConnectionFactory = 234 FullMobileConnectionRepository.Factory( 235 scope = testScope.backgroundScope, 236 logFactory = logBufferFactory, 237 mobileRepoFactory = connectionFactory, 238 carrierMergedRepoFactory = carrierMergedFactory, 239 ) 240 241 underTest = 242 MobileConnectionsRepositoryImpl( 243 connectivityRepository, 244 subscriptionManager, 245 subscriptionManagerProxy, 246 telephonyManager, 247 logger, 248 summaryLogger, 249 mobileMappings, 250 fakeBroadcastDispatcher, 251 context, 252 /* bgDispatcher = */ testDispatcher, 253 testScope.backgroundScope, 254 /* mainDispatcher = */ testDispatcher, 255 airplaneModeRepository, 256 wifiRepository, 257 fullConnectionFactory, 258 updateMonitor, 259 mock(), 260 ) 261 262 testScope.runCurrent() 263 } 264 265 @Test 266 fun testSubscriptions_initiallyEmpty() = 267 testScope.runTest { 268 assertThat(underTest.subscriptions.value).isEqualTo(listOf<SubscriptionModel>()) 269 } 270 271 @Test 272 fun testSubscriptions_listUpdates() = 273 testScope.runTest { 274 val latest by collectLastValue(underTest.subscriptions) 275 276 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 277 .thenReturn(listOf(SUB_1, SUB_2)) 278 getSubscriptionCallback().onSubscriptionsChanged() 279 280 assertThat(latest).isEqualTo(listOf(MODEL_1, MODEL_2)) 281 } 282 283 @Test 284 fun testSubscriptions_removingSub_updatesList() = 285 testScope.runTest { 286 val latest by collectLastValue(underTest.subscriptions) 287 288 // WHEN 2 networks show up 289 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 290 .thenReturn(listOf(SUB_1, SUB_2)) 291 getSubscriptionCallback().onSubscriptionsChanged() 292 293 // WHEN one network is removed 294 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 295 .thenReturn(listOf(SUB_2)) 296 getSubscriptionCallback().onSubscriptionsChanged() 297 298 // THEN the subscriptions list represents the newest change 299 assertThat(latest).isEqualTo(listOf(MODEL_2)) 300 } 301 302 @Test 303 fun subscriptions_subIsOnlyNtn_modelHasExclusivelyNtnTrue() = 304 testScope.runTest { 305 val latest by collectLastValue(underTest.subscriptions) 306 307 val onlyNtnSub = 308 mock<SubscriptionInfo>().also { 309 whenever(it.isOnlyNonTerrestrialNetwork).thenReturn(true) 310 whenever(it.subscriptionId).thenReturn(45) 311 whenever(it.groupUuid).thenReturn(GROUP_1) 312 whenever(it.carrierName).thenReturn("NTN only") 313 whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET) 314 } 315 316 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 317 .thenReturn(listOf(onlyNtnSub)) 318 getSubscriptionCallback().onSubscriptionsChanged() 319 320 assertThat(latest).hasSize(1) 321 assertThat(latest!![0].isExclusivelyNonTerrestrial).isTrue() 322 } 323 324 @Test 325 fun subscriptions_subIsNotOnlyNtn_modelHasExclusivelyNtnFalse() = 326 testScope.runTest { 327 val latest by collectLastValue(underTest.subscriptions) 328 329 val notOnlyNtnSub = 330 mock<SubscriptionInfo>().also { 331 whenever(it.isOnlyNonTerrestrialNetwork).thenReturn(false) 332 whenever(it.subscriptionId).thenReturn(45) 333 whenever(it.groupUuid).thenReturn(GROUP_1) 334 whenever(it.carrierName).thenReturn("NTN only") 335 whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET) 336 } 337 338 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 339 .thenReturn(listOf(notOnlyNtnSub)) 340 getSubscriptionCallback().onSubscriptionsChanged() 341 342 assertThat(latest).hasSize(1) 343 assertThat(latest!![0].isExclusivelyNonTerrestrial).isFalse() 344 } 345 346 @Test 347 fun testSubscriptions_carrierMergedOnly_listHasCarrierMerged() = 348 testScope.runTest { 349 val latest by collectLastValue(underTest.subscriptions) 350 351 setWifiState(isCarrierMerged = true) 352 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 353 .thenReturn(listOf(SUB_CM)) 354 getSubscriptionCallback().onSubscriptionsChanged() 355 356 assertThat(latest).isEqualTo(listOf(MODEL_CM)) 357 } 358 359 @Test 360 fun testSubscriptions_carrierMergedAndOther_listHasBothWithCarrierMergedLast() = 361 testScope.runTest { 362 val latest by collectLastValue(underTest.subscriptions) 363 364 setWifiState(isCarrierMerged = true) 365 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 366 .thenReturn(listOf(SUB_1, SUB_2, SUB_CM)) 367 getSubscriptionCallback().onSubscriptionsChanged() 368 369 assertThat(latest).isEqualTo(listOf(MODEL_1, MODEL_2, MODEL_CM)) 370 } 371 372 @Test 373 fun testActiveDataSubscriptionId_initialValueIsNull() = 374 testScope.runTest { 375 assertThat(underTest.activeMobileDataSubscriptionId.value).isEqualTo(null) 376 } 377 378 @Test 379 fun testActiveDataSubscriptionId_updates() = 380 testScope.runTest { 381 val active by collectLastValue(underTest.activeMobileDataSubscriptionId) 382 383 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 384 .onActiveDataSubscriptionIdChanged(SUB_2_ID) 385 386 assertThat(active).isEqualTo(SUB_2_ID) 387 } 388 389 @Test 390 fun activeSubId_nullIfInvalidSubIdIsReceived() = 391 testScope.runTest { 392 val latest by collectLastValue(underTest.activeMobileDataSubscriptionId) 393 394 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 395 .onActiveDataSubscriptionIdChanged(SUB_2_ID) 396 397 assertThat(latest).isNotNull() 398 399 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 400 .onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID) 401 402 assertThat(latest).isNull() 403 } 404 405 @Test 406 fun activeRepo_initiallyNull() { 407 assertThat(underTest.activeMobileDataRepository.value).isNull() 408 } 409 410 @Test 411 fun activeRepo_updatesWithActiveDataId() = 412 testScope.runTest { 413 val latest by collectLastValue(underTest.activeMobileDataRepository) 414 415 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 416 .onActiveDataSubscriptionIdChanged(SUB_2_ID) 417 418 assertThat(latest?.subId).isEqualTo(SUB_2_ID) 419 } 420 421 @Test 422 fun activeRepo_nullIfActiveDataSubIdBecomesInvalid() = 423 testScope.runTest { 424 val latest by collectLastValue(underTest.activeMobileDataRepository) 425 426 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 427 .onActiveDataSubscriptionIdChanged(SUB_2_ID) 428 429 assertThat(latest).isNotNull() 430 431 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 432 .onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID) 433 434 assertThat(latest).isNull() 435 } 436 437 @Test 438 /** Regression test for b/268146648. */ 439 fun activeSubIdIsSetBeforeSubscriptionsAreUpdated_doesNotThrow() = 440 testScope.runTest { 441 val activeRepo by collectLastValue(underTest.activeMobileDataRepository) 442 val subscriptions by collectLastValue(underTest.subscriptions) 443 444 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 445 .onActiveDataSubscriptionIdChanged(SUB_2_ID) 446 447 assertThat(subscriptions).isEmpty() 448 assertThat(activeRepo).isNotNull() 449 } 450 451 @Test 452 fun getRepoForSubId_activeDataSubIdIsRequestedBeforeSubscriptionsUpdate() = 453 testScope.runTest { 454 var latestActiveRepo: MobileConnectionRepository? = null 455 collectLastValue( 456 underTest.activeMobileDataSubscriptionId.filterNotNull().onEach { 457 latestActiveRepo = underTest.getRepoForSubId(it) 458 } 459 ) 460 461 val latestSubscriptions by collectLastValue(underTest.subscriptions) 462 463 // Active data subscription id is sent, but no subscription change has been posted yet 464 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 465 .onActiveDataSubscriptionIdChanged(SUB_2_ID) 466 467 // Subscriptions list is empty 468 assertThat(latestSubscriptions).isEmpty() 469 // getRepoForSubId does not throw 470 assertThat(latestActiveRepo).isNotNull() 471 } 472 473 @Test 474 fun activeDataSentBeforeSubscriptionList_subscriptionReusesActiveDataRepo() = 475 testScope.runTest { 476 val activeRepo by collectLastValue(underTest.activeMobileDataRepository) 477 collectLastValue(underTest.subscriptions) 478 479 // GIVEN active repo is updated before the subscription list updates 480 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 481 .onActiveDataSubscriptionIdChanged(SUB_2_ID) 482 483 assertThat(activeRepo).isNotNull() 484 485 // GIVEN the subscription list is then updated which includes the active data sub id 486 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 487 .thenReturn(listOf(SUB_2)) 488 getSubscriptionCallback().onSubscriptionsChanged() 489 490 // WHEN requesting a connection repository for the subscription 491 val newRepo = underTest.getRepoForSubId(SUB_2_ID) 492 493 // THEN the newly request repo has been cached and reused 494 assertThat(activeRepo).isSameInstanceAs(newRepo) 495 } 496 497 @Test 498 fun testConnectionRepository_validSubId_isCached() = 499 testScope.runTest { 500 collectLastValue(underTest.subscriptions) 501 502 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 503 .thenReturn(listOf(SUB_1)) 504 getSubscriptionCallback().onSubscriptionsChanged() 505 506 val repo1 = underTest.getRepoForSubId(SUB_1_ID) 507 val repo2 = underTest.getRepoForSubId(SUB_1_ID) 508 509 assertThat(repo1).isSameInstanceAs(repo2) 510 } 511 512 @Test 513 fun testConnectionRepository_carrierMergedSubId_isCached() = 514 testScope.runTest { 515 collectLastValue(underTest.subscriptions) 516 517 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) 518 setWifiState(isCarrierMerged = true) 519 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 520 .thenReturn(listOf(SUB_CM)) 521 getSubscriptionCallback().onSubscriptionsChanged() 522 523 val repo1 = underTest.getRepoForSubId(SUB_CM_ID) 524 val repo2 = underTest.getRepoForSubId(SUB_CM_ID) 525 526 assertThat(repo1).isSameInstanceAs(repo2) 527 } 528 529 @Test 530 fun testConnectionRepository_carrierMergedAndMobileSubs_usesCorrectRepos() = 531 testScope.runTest { 532 collectLastValue(underTest.subscriptions) 533 534 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) 535 setWifiState(isCarrierMerged = true) 536 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 537 .thenReturn(listOf(SUB_1, SUB_CM)) 538 getSubscriptionCallback().onSubscriptionsChanged() 539 540 val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) 541 val mobileRepo = underTest.getRepoForSubId(SUB_1_ID) 542 assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue() 543 assertThat(mobileRepo.getIsCarrierMerged()).isFalse() 544 } 545 546 @Test 547 fun testSubscriptions_subNoLongerCarrierMerged_repoUpdates() = 548 testScope.runTest { 549 collectLastValue(underTest.subscriptions) 550 551 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) 552 setWifiState(isCarrierMerged = true) 553 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 554 .thenReturn(listOf(SUB_1, SUB_CM)) 555 getSubscriptionCallback().onSubscriptionsChanged() 556 557 val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) 558 var mobileRepo = underTest.getRepoForSubId(SUB_1_ID) 559 assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue() 560 assertThat(mobileRepo.getIsCarrierMerged()).isFalse() 561 562 // WHEN the wifi network updates to be not carrier merged 563 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE) 564 setWifiState(isCarrierMerged = false) 565 runCurrent() 566 567 // THEN the repos update 568 val noLongerCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) 569 mobileRepo = underTest.getRepoForSubId(SUB_1_ID) 570 assertThat(noLongerCarrierMergedRepo.getIsCarrierMerged()).isFalse() 571 assertThat(mobileRepo.getIsCarrierMerged()).isFalse() 572 } 573 574 @Test 575 fun testSubscriptions_subBecomesCarrierMerged_repoUpdates() = 576 testScope.runTest { 577 collectLastValue(underTest.subscriptions) 578 579 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE) 580 setWifiState(isCarrierMerged = false) 581 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 582 .thenReturn(listOf(SUB_1, SUB_CM)) 583 getSubscriptionCallback().onSubscriptionsChanged() 584 runCurrent() 585 586 val notYetCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) 587 var mobileRepo = underTest.getRepoForSubId(SUB_1_ID) 588 assertThat(notYetCarrierMergedRepo.getIsCarrierMerged()).isFalse() 589 assertThat(mobileRepo.getIsCarrierMerged()).isFalse() 590 591 // WHEN the wifi network updates to be carrier merged 592 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) 593 setWifiState(isCarrierMerged = true) 594 runCurrent() 595 596 // THEN the repos update 597 val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID) 598 mobileRepo = underTest.getRepoForSubId(SUB_1_ID) 599 assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue() 600 assertThat(mobileRepo.getIsCarrierMerged()).isFalse() 601 } 602 603 @SuppressLint("UnspecifiedRegisterReceiverFlag") 604 @Test 605 fun testDeviceServiceStateFromBroadcast_eagerlyWatchesBroadcast() = 606 testScope.runTest { 607 // Value starts out empty (null) 608 assertThat(underTest.deviceServiceState.value).isNull() 609 610 // WHEN an appropriate intent gets sent out 611 val intent = serviceStateIntent(subId = -1, emergencyOnly = false) 612 fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( 613 context, 614 intent, 615 ) 616 runCurrent() 617 618 // THEN the repo's state is updated 619 val expected = ServiceStateModel(isEmergencyOnly = false) 620 assertThat(underTest.deviceServiceState.value).isEqualTo(expected) 621 } 622 623 @Test 624 fun testDeviceServiceStateFromBroadcast_followsSubIdNegativeOne() = 625 testScope.runTest { 626 // device based state tracks -1 627 val intent = serviceStateIntent(subId = -1, emergencyOnly = false) 628 fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( 629 context, 630 intent, 631 ) 632 runCurrent() 633 634 val deviceBasedState = ServiceStateModel(isEmergencyOnly = false) 635 assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState) 636 637 // ... and ignores any other subId 638 val intent2 = serviceStateIntent(subId = 1, emergencyOnly = true) 639 fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( 640 context, 641 intent2, 642 ) 643 runCurrent() 644 645 assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState) 646 } 647 648 @Test 649 @Ignore("b/333912012") 650 fun testConnectionCache_clearsInvalidSubscriptions() = 651 testScope.runTest { 652 collectLastValue(underTest.subscriptions) 653 654 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 655 .thenReturn(listOf(SUB_1, SUB_2)) 656 getSubscriptionCallback().onSubscriptionsChanged() 657 658 // Get repos to trigger caching 659 val repo1 = underTest.getRepoForSubId(SUB_1_ID) 660 val repo2 = underTest.getRepoForSubId(SUB_2_ID) 661 662 assertThat(underTest.getSubIdRepoCache()) 663 .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2) 664 665 // SUB_2 disappears 666 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 667 .thenReturn(listOf(SUB_1)) 668 getSubscriptionCallback().onSubscriptionsChanged() 669 670 assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1) 671 } 672 673 @Test 674 @Ignore("b/333912012") 675 fun testConnectionCache_clearsInvalidSubscriptions_includingCarrierMerged() = 676 testScope.runTest { 677 collectLastValue(underTest.subscriptions) 678 679 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM) 680 setWifiState(isCarrierMerged = true) 681 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 682 .thenReturn(listOf(SUB_1, SUB_2, SUB_CM)) 683 getSubscriptionCallback().onSubscriptionsChanged() 684 685 // Get repos to trigger caching 686 val repo1 = underTest.getRepoForSubId(SUB_1_ID) 687 val repo2 = underTest.getRepoForSubId(SUB_2_ID) 688 val repoCarrierMerged = underTest.getRepoForSubId(SUB_CM_ID) 689 690 assertThat(underTest.getSubIdRepoCache()) 691 .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2, SUB_CM_ID, repoCarrierMerged) 692 693 // SUB_2 and SUB_CM disappear 694 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 695 .thenReturn(listOf(SUB_1)) 696 getSubscriptionCallback().onSubscriptionsChanged() 697 698 assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1) 699 } 700 701 /** Regression test for b/261706421 */ 702 @Test 703 @Ignore("b/333912012") 704 fun testConnectionsCache_clearMultipleSubscriptionsAtOnce_doesNotThrow() = 705 testScope.runTest { 706 collectLastValue(underTest.subscriptions) 707 708 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 709 .thenReturn(listOf(SUB_1, SUB_2)) 710 getSubscriptionCallback().onSubscriptionsChanged() 711 712 // Get repos to trigger caching 713 val repo1 = underTest.getRepoForSubId(SUB_1_ID) 714 val repo2 = underTest.getRepoForSubId(SUB_2_ID) 715 716 assertThat(underTest.getSubIdRepoCache()) 717 .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2) 718 719 // All subscriptions disappear 720 whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf()) 721 getSubscriptionCallback().onSubscriptionsChanged() 722 723 assertThat(underTest.getSubIdRepoCache()).isEmpty() 724 } 725 726 @Test 727 fun testConnectionsCache_keepsReposCached() = 728 testScope.runTest { 729 // Collect subscriptions to start the job 730 collectLastValue(underTest.subscriptions) 731 732 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 733 .thenReturn(listOf(SUB_1)) 734 getSubscriptionCallback().onSubscriptionsChanged() 735 736 val repo1_1 = underTest.getRepoForSubId(SUB_1_ID) 737 738 // All subscriptions disappear 739 whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf()) 740 getSubscriptionCallback().onSubscriptionsChanged() 741 742 // Sub1 comes back 743 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 744 .thenReturn(listOf(SUB_1)) 745 getSubscriptionCallback().onSubscriptionsChanged() 746 747 val repo1_2 = underTest.getRepoForSubId(SUB_1_ID) 748 749 assertThat(repo1_1).isSameInstanceAs(repo1_2) 750 } 751 752 @Test 753 fun testConnectionsCache_doesNotDropReferencesThatHaveBeenRealized() = 754 testScope.runTest { 755 // Collect subscriptions to start the job 756 collectLastValue(underTest.subscriptions) 757 758 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 759 .thenReturn(listOf(SUB_1)) 760 getSubscriptionCallback().onSubscriptionsChanged() 761 762 // Client grabs a reference to a repository, but doesn't keep it around 763 underTest.getRepoForSubId(SUB_1_ID) 764 765 // All subscriptions disappear 766 whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf()) 767 getSubscriptionCallback().onSubscriptionsChanged() 768 769 val repo1 = underTest.getRepoForSubId(SUB_1_ID) 770 771 assertThat(repo1).isNotNull() 772 } 773 774 @Test 775 fun testConnectionRepository_invalidSubId_doesNotThrow() = 776 testScope.runTest { 777 underTest.getRepoForSubId(SUB_1_ID) 778 // No exception 779 } 780 781 @Test 782 fun connectionRepository_logBufferContainsSubIdInItsName() = 783 testScope.runTest { 784 collectLastValue(underTest.subscriptions) 785 786 whenever(subscriptionManager.completeActiveSubscriptionInfoList) 787 .thenReturn(listOf(SUB_1, SUB_2)) 788 getSubscriptionCallback().onSubscriptionsChanged() 789 790 // Get repos to trigger creation 791 underTest.getRepoForSubId(SUB_1_ID) 792 verify(logBufferFactory) 793 .getOrCreate( 794 eq(tableBufferLogName(SUB_1_ID)), 795 anyInt(), 796 ) 797 underTest.getRepoForSubId(SUB_2_ID) 798 verify(logBufferFactory) 799 .getOrCreate( 800 eq(tableBufferLogName(SUB_2_ID)), 801 anyInt(), 802 ) 803 } 804 805 @Test 806 fun testDefaultDataSubId_updatesOnBroadcast() = 807 testScope.runTest { 808 val latest by collectLastValue(underTest.defaultDataSubId) 809 810 assertThat(latest).isEqualTo(INVALID_SUBSCRIPTION_ID) 811 812 val intent2 = 813 Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) 814 .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_2_ID) 815 fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent2) 816 817 assertThat(latest).isEqualTo(SUB_2_ID) 818 819 val intent1 = 820 Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) 821 .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_1_ID) 822 fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent1) 823 824 assertThat(latest).isEqualTo(SUB_1_ID) 825 } 826 827 @Test 828 fun defaultDataSubId_fetchesInitialValueOnStart() = 829 testScope.runTest { 830 subscriptionManagerProxy.defaultDataSubId = 2 831 val latest by collectLastValue(underTest.defaultDataSubId) 832 833 assertThat(latest).isEqualTo(2) 834 } 835 836 @Test 837 fun defaultDataSubId_fetchesCurrentOnRestart() = 838 testScope.runTest { 839 subscriptionManagerProxy.defaultDataSubId = 2 840 var latest: Int? = null 841 var job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this) 842 runCurrent() 843 844 assertThat(latest).isEqualTo(2) 845 846 job.cancel() 847 848 // Collectors go away but come back later 849 850 latest = null 851 852 subscriptionManagerProxy.defaultDataSubId = 1 853 854 job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this) 855 runCurrent() 856 857 assertThat(latest).isEqualTo(1) 858 859 job.cancel() 860 } 861 862 @Test 863 fun mobileIsDefault_startsAsFalse() { 864 assertThat(underTest.mobileIsDefault.value).isFalse() 865 } 866 867 @Test 868 fun mobileIsDefault_capsHaveCellular_isDefault() = 869 testScope.runTest { 870 val caps = 871 mock<NetworkCapabilities>().also { 872 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true) 873 } 874 875 val latest by collectLastValue(underTest.mobileIsDefault) 876 877 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 878 879 assertThat(latest).isTrue() 880 } 881 882 @Test 883 fun mobileIsDefault_capsDoNotHaveCellular_isNotDefault() = 884 testScope.runTest { 885 val caps = 886 mock<NetworkCapabilities>().also { 887 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false) 888 } 889 890 val latest by collectLastValue(underTest.mobileIsDefault) 891 892 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 893 894 assertThat(latest).isFalse() 895 } 896 897 @Test 898 fun mobileIsDefault_carrierMergedViaMobile_isDefault() = 899 testScope.runTest { 900 val carrierMergedInfo = 901 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) } 902 val caps = 903 mock<NetworkCapabilities>().also { 904 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true) 905 whenever(it.transportInfo).thenReturn(carrierMergedInfo) 906 } 907 908 val latest by collectLastValue(underTest.mobileIsDefault) 909 910 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 911 912 assertThat(latest).isTrue() 913 } 914 915 @Test 916 fun mobileIsDefault_wifiDefault_mobileNotDefault() = 917 testScope.runTest { 918 val caps = 919 mock<NetworkCapabilities>().also { 920 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true) 921 } 922 923 val latest by collectLastValue(underTest.mobileIsDefault) 924 925 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 926 927 assertThat(latest).isFalse() 928 } 929 930 @Test 931 fun mobileIsDefault_ethernetDefault_mobileNotDefault() = 932 testScope.runTest { 933 val caps = 934 mock<NetworkCapabilities>().also { 935 whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(true) 936 } 937 938 val latest by collectLastValue(underTest.mobileIsDefault) 939 940 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 941 942 assertThat(latest).isFalse() 943 } 944 945 /** Regression test for b/272586234. */ 946 @Test 947 fun hasCarrierMergedConnection_carrierMergedViaWifi_isTrue() = 948 testScope.runTest { 949 val carrierMergedInfo = 950 mock<WifiInfo>().apply { 951 whenever(this.isCarrierMerged).thenReturn(true) 952 whenever(this.isPrimary).thenReturn(true) 953 } 954 val caps = 955 mock<NetworkCapabilities>().also { 956 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true) 957 whenever(it.transportInfo).thenReturn(carrierMergedInfo) 958 } 959 960 val latest by collectLastValue(underTest.hasCarrierMergedConnection) 961 962 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 963 setWifiState(isCarrierMerged = true) 964 965 assertThat(latest).isTrue() 966 } 967 968 @Test 969 fun hasCarrierMergedConnection_carrierMergedViaMobile_isTrue() = 970 testScope.runTest { 971 val carrierMergedInfo = 972 mock<WifiInfo>().apply { 973 whenever(this.isCarrierMerged).thenReturn(true) 974 whenever(this.isPrimary).thenReturn(true) 975 } 976 val caps = 977 mock<NetworkCapabilities>().also { 978 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true) 979 whenever(it.transportInfo).thenReturn(carrierMergedInfo) 980 } 981 982 val latest by collectLastValue(underTest.hasCarrierMergedConnection) 983 984 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 985 setWifiState(isCarrierMerged = true) 986 987 assertThat(latest).isTrue() 988 } 989 990 /** Regression test for b/272586234. */ 991 @Test 992 fun hasCarrierMergedConnection_carrierMergedViaWifiWithVcnTransport_isTrue() = 993 testScope.runTest { 994 val carrierMergedInfo = 995 mock<WifiInfo>().apply { 996 whenever(this.isCarrierMerged).thenReturn(true) 997 whenever(this.isPrimary).thenReturn(true) 998 } 999 val caps = 1000 mock<NetworkCapabilities>().also { 1001 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true) 1002 whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo)) 1003 } 1004 1005 val latest by collectLastValue(underTest.hasCarrierMergedConnection) 1006 1007 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 1008 setWifiState(isCarrierMerged = true) 1009 1010 assertThat(latest).isTrue() 1011 } 1012 1013 @Test 1014 fun hasCarrierMergedConnection_carrierMergedViaMobileWithVcnTransport_isTrue() = 1015 testScope.runTest { 1016 val carrierMergedInfo = 1017 mock<WifiInfo>().apply { 1018 whenever(this.isCarrierMerged).thenReturn(true) 1019 whenever(this.isPrimary).thenReturn(true) 1020 } 1021 val caps = 1022 mock<NetworkCapabilities>().also { 1023 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true) 1024 whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo)) 1025 } 1026 1027 val latest by collectLastValue(underTest.hasCarrierMergedConnection) 1028 1029 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 1030 setWifiState(isCarrierMerged = true) 1031 1032 assertThat(latest).isTrue() 1033 } 1034 1035 @Test 1036 fun hasCarrierMergedConnection_isCarrierMergedViaUnderlyingWifi_isTrue() = 1037 testScope.runTest { 1038 val latest by collectLastValue(underTest.hasCarrierMergedConnection) 1039 1040 val underlyingNetwork = mock<Network>() 1041 val carrierMergedInfo = 1042 mock<WifiInfo>().apply { 1043 whenever(this.isCarrierMerged).thenReturn(true) 1044 whenever(this.isPrimary).thenReturn(true) 1045 } 1046 val underlyingWifiCapabilities = 1047 mock<NetworkCapabilities>().also { 1048 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true) 1049 whenever(it.transportInfo).thenReturn(carrierMergedInfo) 1050 } 1051 whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork)) 1052 .thenReturn(underlyingWifiCapabilities) 1053 1054 // WHEN the main capabilities have an underlying carrier merged network via WIFI 1055 // transport and WifiInfo 1056 val mainCapabilities = 1057 mock<NetworkCapabilities>().also { 1058 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true) 1059 whenever(it.transportInfo).thenReturn(null) 1060 whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork)) 1061 } 1062 1063 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities) 1064 setWifiState(isCarrierMerged = true) 1065 1066 // THEN there's a carrier merged connection 1067 assertThat(latest).isTrue() 1068 } 1069 1070 @Test 1071 fun hasCarrierMergedConnection_isCarrierMergedViaUnderlyingCellular_isTrue() = 1072 testScope.runTest { 1073 val latest by collectLastValue(underTest.hasCarrierMergedConnection) 1074 1075 val underlyingCarrierMergedNetwork = mock<Network>() 1076 val carrierMergedInfo = 1077 mock<WifiInfo>().apply { 1078 whenever(this.isCarrierMerged).thenReturn(true) 1079 whenever(this.isPrimary).thenReturn(true) 1080 } 1081 val underlyingCapabilities = 1082 mock<NetworkCapabilities>().also { 1083 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true) 1084 whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo)) 1085 } 1086 whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork)) 1087 .thenReturn(underlyingCapabilities) 1088 1089 // WHEN the main capabilities have an underlying carrier merged network via CELLULAR 1090 // transport and VcnTransportInfo 1091 val mainCapabilities = 1092 mock<NetworkCapabilities>().also { 1093 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true) 1094 whenever(it.transportInfo).thenReturn(null) 1095 whenever(it.underlyingNetworks) 1096 .thenReturn(listOf(underlyingCarrierMergedNetwork)) 1097 } 1098 1099 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities) 1100 setWifiState(isCarrierMerged = true) 1101 1102 // THEN there's a carrier merged connection 1103 assertThat(latest).isTrue() 1104 } 1105 1106 /** Regression test for b/272586234. */ 1107 @Test 1108 fun hasCarrierMergedConnection_defaultIsWifiNotCarrierMerged_wifiRepoIsCarrierMerged_isTrue() = 1109 testScope.runTest { 1110 val latest by collectLastValue(underTest.hasCarrierMergedConnection) 1111 1112 // WHEN the default callback is TRANSPORT_WIFI but not carrier merged 1113 val carrierMergedInfo = 1114 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(false) } 1115 val caps = 1116 mock<NetworkCapabilities>().also { 1117 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true) 1118 whenever(it.transportInfo).thenReturn(carrierMergedInfo) 1119 } 1120 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 1121 1122 // BUT the wifi repo has gotten updates that it *is* carrier merged 1123 setWifiState(isCarrierMerged = true) 1124 1125 // THEN hasCarrierMergedConnection is true 1126 assertThat(latest).isTrue() 1127 } 1128 1129 /** Regression test for b/278618530. */ 1130 @Test 1131 fun hasCarrierMergedConnection_defaultIsCellular_wifiRepoIsCarrierMerged_isFalse() = 1132 testScope.runTest { 1133 val latest by collectLastValue(underTest.hasCarrierMergedConnection) 1134 1135 // WHEN the default callback is TRANSPORT_CELLULAR and not carrier merged 1136 val caps = 1137 mock<NetworkCapabilities>().also { 1138 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true) 1139 whenever(it.transportInfo).thenReturn(null) 1140 } 1141 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 1142 1143 // BUT the wifi repo has gotten updates that it *is* carrier merged 1144 setWifiState(isCarrierMerged = true) 1145 1146 // THEN hasCarrierMergedConnection is **false** (The default network being CELLULAR 1147 // takes precedence over the wifi network being carrier merged.) 1148 assertThat(latest).isFalse() 1149 } 1150 1151 /** Regression test for b/278618530. */ 1152 @Test 1153 fun hasCarrierMergedConnection_defaultCellular_wifiIsCarrierMerged_airplaneMode_isTrue() = 1154 testScope.runTest { 1155 val latest by collectLastValue(underTest.hasCarrierMergedConnection) 1156 1157 // WHEN the default callback is TRANSPORT_CELLULAR and not carrier merged 1158 val caps = 1159 mock<NetworkCapabilities>().also { 1160 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true) 1161 whenever(it.transportInfo).thenReturn(null) 1162 } 1163 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 1164 1165 // BUT the wifi repo has gotten updates that it *is* carrier merged 1166 setWifiState(isCarrierMerged = true) 1167 // AND we're in airplane mode 1168 airplaneModeRepository.setIsAirplaneMode(true) 1169 1170 // THEN hasCarrierMergedConnection is true. 1171 assertThat(latest).isTrue() 1172 } 1173 1174 @Test 1175 fun defaultConnectionIsValidated_startsAsFalse() { 1176 assertThat(underTest.defaultConnectionIsValidated.value).isFalse() 1177 } 1178 1179 @Test 1180 fun defaultConnectionIsValidated_capsHaveValidated_isValidated() = 1181 testScope.runTest { 1182 val caps = 1183 mock<NetworkCapabilities>().also { 1184 whenever(it.hasCapability(NET_CAPABILITY_VALIDATED)).thenReturn(true) 1185 } 1186 1187 val latest by collectLastValue(underTest.defaultConnectionIsValidated) 1188 1189 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 1190 1191 assertThat(latest).isTrue() 1192 } 1193 1194 @Test 1195 fun defaultConnectionIsValidated_capsHaveNotValidated_isNotValidated() = 1196 testScope.runTest { 1197 val caps = 1198 mock<NetworkCapabilities>().also { 1199 whenever(it.hasCapability(NET_CAPABILITY_VALIDATED)).thenReturn(false) 1200 } 1201 1202 val latest by collectLastValue(underTest.defaultConnectionIsValidated) 1203 1204 getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps) 1205 1206 assertThat(latest).isFalse() 1207 } 1208 1209 @Test 1210 fun config_initiallyFromContext() = 1211 testScope.runTest { 1212 overrideResource(R.bool.config_showMin3G, true) 1213 val configFromContext = MobileMappings.Config.readConfig(context) 1214 assertThat(configFromContext.showAtLeast3G).isTrue() 1215 1216 // The initial value will be fetched when the repo is created, so we need to override 1217 // the resources and then re-create the repo. 1218 underTest = 1219 MobileConnectionsRepositoryImpl( 1220 connectivityRepository, 1221 subscriptionManager, 1222 subscriptionManagerProxy, 1223 telephonyManager, 1224 logger, 1225 summaryLogger, 1226 mobileMappings, 1227 fakeBroadcastDispatcher, 1228 context, 1229 testDispatcher, 1230 testScope.backgroundScope, 1231 testDispatcher, 1232 airplaneModeRepository, 1233 wifiRepository, 1234 fullConnectionFactory, 1235 updateMonitor, 1236 mock(), 1237 ) 1238 1239 val latest by collectLastValue(underTest.defaultDataSubRatConfig) 1240 1241 assertTrue(latest!!.areEqual(configFromContext)) 1242 assertTrue(latest!!.showAtLeast3G) 1243 } 1244 1245 @Test 1246 fun config_subIdChangeEvent_updated() = 1247 testScope.runTest { 1248 val latest by collectLastValue(underTest.defaultDataSubRatConfig) 1249 1250 assertThat(latest!!.showAtLeast3G).isFalse() 1251 1252 overrideResource(R.bool.config_showMin3G, true) 1253 val configFromContext = MobileMappings.Config.readConfig(context) 1254 assertThat(configFromContext.showAtLeast3G).isTrue() 1255 1256 // WHEN the change event is fired 1257 val intent = 1258 Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) 1259 .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_1_ID) 1260 fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent) 1261 1262 // THEN the config is updated 1263 assertTrue(latest!!.areEqual(configFromContext)) 1264 assertTrue(latest!!.showAtLeast3G) 1265 } 1266 1267 @Test 1268 fun config_carrierConfigChangeEvent_updated() = 1269 testScope.runTest { 1270 val latest by collectLastValue(underTest.defaultDataSubRatConfig) 1271 1272 assertThat(latest!!.showAtLeast3G).isFalse() 1273 1274 overrideResource(R.bool.config_showMin3G, true) 1275 val configFromContext = MobileMappings.Config.readConfig(context) 1276 assertThat(configFromContext.showAtLeast3G).isTrue() 1277 1278 // WHEN the change event is fired 1279 fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( 1280 context, 1281 Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED), 1282 ) 1283 1284 // THEN the config is updated 1285 assertThat(latest!!.areEqual(configFromContext)).isTrue() 1286 assertThat(latest!!.showAtLeast3G).isTrue() 1287 } 1288 1289 @Test 1290 fun carrierConfig_initialValueIsFetched() = 1291 testScope.runTest { 1292 // Value starts out false 1293 assertThat(underTest.defaultDataSubRatConfig.value.showAtLeast3G).isFalse() 1294 1295 overrideResource(R.bool.config_showMin3G, true) 1296 val configFromContext = MobileMappings.Config.readConfig(context) 1297 assertThat(configFromContext.showAtLeast3G).isTrue() 1298 1299 // WHEN the change event is fired 1300 fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( 1301 context, 1302 Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED), 1303 ) 1304 1305 // WHEN collection starts AFTER the broadcast is sent out 1306 val latest by collectLastValue(underTest.defaultDataSubRatConfig) 1307 1308 // THEN the config has the updated value 1309 assertThat(latest!!.areEqual(configFromContext)).isTrue() 1310 assertThat(latest!!.showAtLeast3G).isTrue() 1311 } 1312 1313 @Test 1314 fun activeDataChange_inSameGroup_emitsUnit() = 1315 testScope.runTest { 1316 val latest by collectLastValue(underTest.activeSubChangedInGroupEvent) 1317 1318 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 1319 .onActiveDataSubscriptionIdChanged(SUB_3_ID_GROUPED) 1320 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 1321 .onActiveDataSubscriptionIdChanged(SUB_4_ID_GROUPED) 1322 1323 assertThat(latest).isEqualTo(Unit) 1324 } 1325 1326 @Test 1327 fun activeDataChange_notInSameGroup_doesNotEmit() = 1328 testScope.runTest { 1329 val latest by collectLastValue(underTest.activeSubChangedInGroupEvent) 1330 1331 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 1332 .onActiveDataSubscriptionIdChanged(SUB_3_ID_GROUPED) 1333 getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>() 1334 .onActiveDataSubscriptionIdChanged(SUB_1_ID) 1335 1336 assertThat(latest).isEqualTo(null) 1337 } 1338 1339 @Test 1340 fun anySimSecure_propagatesStateFromKeyguardUpdateMonitor() = 1341 testScope.runTest { 1342 val latest by collectLastValue(underTest.isAnySimSecure) 1343 assertThat(latest).isFalse() 1344 1345 val updateMonitorCallback = argumentCaptor<KeyguardUpdateMonitorCallback>() 1346 verify(updateMonitor).registerCallback(updateMonitorCallback.capture()) 1347 1348 whenever(updateMonitor.isSimPinSecure).thenReturn(true) 1349 updateMonitorCallback.value.onSimStateChanged(0, 0, 0) 1350 1351 assertThat(latest).isTrue() 1352 1353 whenever(updateMonitor.isSimPinSecure).thenReturn(false) 1354 updateMonitorCallback.value.onSimStateChanged(0, 0, 0) 1355 1356 assertThat(latest).isFalse() 1357 } 1358 1359 @Test 1360 fun getIsAnySimSecure_delegatesCallToKeyguardUpdateMonitor() = 1361 testScope.runTest { 1362 assertThat(underTest.getIsAnySimSecure()).isFalse() 1363 1364 whenever(updateMonitor.isSimPinSecure).thenReturn(true) 1365 1366 assertThat(underTest.getIsAnySimSecure()).isTrue() 1367 } 1368 1369 @Test 1370 fun noSubscriptionsInEcmMode_notInEcmMode() = 1371 testScope.runTest { 1372 whenever(telephonyManager.emergencyCallbackMode).thenReturn(false) 1373 1374 runCurrent() 1375 1376 assertThat(underTest.isInEcmMode()).isFalse() 1377 } 1378 1379 @Test 1380 fun someSubscriptionsInEcmMode_inEcmMode() = 1381 testScope.runTest { 1382 whenever(telephonyManager.emergencyCallbackMode).thenReturn(true) 1383 1384 runCurrent() 1385 1386 assertThat(underTest.isInEcmMode()).isTrue() 1387 } 1388 1389 private fun TestScope.getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback { 1390 runCurrent() 1391 val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>() 1392 verify(connectivityManager).registerDefaultNetworkCallback(callbackCaptor.capture()) 1393 return callbackCaptor.value!! 1394 } 1395 1396 private fun setWifiState(isCarrierMerged: Boolean) { 1397 if (isCarrierMerged) { 1398 val mergedEntry = 1399 mock<MergedCarrierEntry>().apply { 1400 whenever(this.isPrimaryNetwork).thenReturn(true) 1401 whenever(this.isDefaultNetwork).thenReturn(true) 1402 whenever(this.subscriptionId).thenReturn(SUB_CM_ID) 1403 } 1404 whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry) 1405 whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null) 1406 } else { 1407 val wifiEntry = 1408 mock<WifiEntry>().apply { 1409 whenever(this.isPrimaryNetwork).thenReturn(true) 1410 whenever(this.isDefaultNetwork).thenReturn(true) 1411 } 1412 whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry) 1413 whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(null) 1414 } 1415 wifiPickerTrackerCallback.value.onWifiEntriesChanged() 1416 } 1417 1418 private fun TestScope.getSubscriptionCallback(): 1419 SubscriptionManager.OnSubscriptionsChangedListener { 1420 runCurrent() 1421 val callbackCaptor = argumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener>() 1422 verify(subscriptionManager) 1423 .addOnSubscriptionsChangedListener(any(), callbackCaptor.capture()) 1424 return callbackCaptor.value!! 1425 } 1426 1427 private fun TestScope.getTelephonyCallbacks(): List<TelephonyCallback> { 1428 runCurrent() 1429 val callbackCaptor = argumentCaptor<TelephonyCallback>() 1430 verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture()) 1431 return callbackCaptor.allValues 1432 } 1433 1434 private inline fun <reified T> TestScope.getTelephonyCallbackForType(): T { 1435 val cbs = this.getTelephonyCallbacks().filterIsInstance<T>() 1436 assertThat(cbs.size).isEqualTo(1) 1437 return cbs[0] 1438 } 1439 1440 companion object { 1441 // Subscription 1 1442 private const val SUB_1_ID = 1 1443 private const val SUB_1_NAME = "Carrier $SUB_1_ID" 1444 private val GROUP_1 = ParcelUuid(UUID.randomUUID()) 1445 private val SUB_1 = 1446 mock<SubscriptionInfo>().also { 1447 whenever(it.subscriptionId).thenReturn(SUB_1_ID) 1448 whenever(it.groupUuid).thenReturn(GROUP_1) 1449 whenever(it.carrierName).thenReturn(SUB_1_NAME) 1450 whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET) 1451 } 1452 private val MODEL_1 = 1453 SubscriptionModel( 1454 subscriptionId = SUB_1_ID, 1455 groupUuid = GROUP_1, 1456 carrierName = SUB_1_NAME, 1457 profileClass = PROFILE_CLASS_UNSET, 1458 ) 1459 1460 // Subscription 2 1461 private const val SUB_2_ID = 2 1462 private const val SUB_2_NAME = "Carrier $SUB_2_ID" 1463 private val GROUP_2 = ParcelUuid(UUID.randomUUID()) 1464 private val SUB_2 = 1465 mock<SubscriptionInfo>().also { 1466 whenever(it.subscriptionId).thenReturn(SUB_2_ID) 1467 whenever(it.groupUuid).thenReturn(GROUP_2) 1468 whenever(it.carrierName).thenReturn(SUB_2_NAME) 1469 whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET) 1470 } 1471 private val MODEL_2 = 1472 SubscriptionModel( 1473 subscriptionId = SUB_2_ID, 1474 groupUuid = GROUP_2, 1475 carrierName = SUB_2_NAME, 1476 profileClass = PROFILE_CLASS_UNSET, 1477 ) 1478 1479 // Subs 3 and 4 are considered to be in the same group ------------------------------------ 1480 private val GROUP_ID_3_4 = ParcelUuid(UUID.randomUUID()) 1481 1482 // Subscription 3 1483 private const val SUB_3_ID_GROUPED = 3 1484 private val SUB_3 = 1485 mock<SubscriptionInfo>().also { 1486 whenever(it.subscriptionId).thenReturn(SUB_3_ID_GROUPED) 1487 whenever(it.groupUuid).thenReturn(GROUP_ID_3_4) 1488 whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET) 1489 } 1490 1491 // Subscription 4 1492 private const val SUB_4_ID_GROUPED = 4 1493 private val SUB_4 = 1494 mock<SubscriptionInfo>().also { 1495 whenever(it.subscriptionId).thenReturn(SUB_4_ID_GROUPED) 1496 whenever(it.groupUuid).thenReturn(GROUP_ID_3_4) 1497 whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET) 1498 } 1499 1500 // Subs 3 and 4 are considered to be in the same group ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1501 1502 private const val NET_ID = 123 1503 private val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) } 1504 1505 // Carrier merged subscription 1506 private const val SUB_CM_ID = 5 1507 private const val SUB_CM_NAME = "Carrier $SUB_CM_ID" 1508 private val SUB_CM = 1509 mock<SubscriptionInfo>().also { 1510 whenever(it.subscriptionId).thenReturn(SUB_CM_ID) 1511 whenever(it.carrierName).thenReturn(SUB_CM_NAME) 1512 whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET) 1513 } 1514 private val MODEL_CM = 1515 SubscriptionModel( 1516 subscriptionId = SUB_CM_ID, 1517 carrierName = SUB_CM_NAME, 1518 profileClass = PROFILE_CLASS_UNSET, 1519 ) 1520 1521 private val WIFI_INFO_CM = 1522 mock<WifiInfo>().apply { 1523 whenever(this.isPrimary).thenReturn(true) 1524 whenever(this.isCarrierMerged).thenReturn(true) 1525 whenever(this.subscriptionId).thenReturn(SUB_CM_ID) 1526 } 1527 private val WIFI_NETWORK_CAPS_CM = 1528 mock<NetworkCapabilities>().also { 1529 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true) 1530 whenever(it.transportInfo).thenReturn(WIFI_INFO_CM) 1531 whenever(it.hasCapability(NET_CAPABILITY_VALIDATED)).thenReturn(true) 1532 } 1533 1534 private val WIFI_INFO_ACTIVE = 1535 mock<WifiInfo>().apply { 1536 whenever(this.isPrimary).thenReturn(true) 1537 whenever(this.isCarrierMerged).thenReturn(false) 1538 } 1539 private val WIFI_NETWORK_CAPS_ACTIVE = 1540 mock<NetworkCapabilities>().also { 1541 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true) 1542 whenever(it.transportInfo).thenReturn(WIFI_INFO_ACTIVE) 1543 whenever(it.hasCapability(NET_CAPABILITY_VALIDATED)).thenReturn(true) 1544 } 1545 1546 /** 1547 * To properly mimic telephony manager, create a service state, and then turn it into an 1548 * intent 1549 */ 1550 private fun serviceStateIntent( 1551 subId: Int, 1552 emergencyOnly: Boolean = false, 1553 ): Intent { 1554 val serviceState = ServiceState().apply { isEmergencyOnly = emergencyOnly } 1555 1556 val bundle = Bundle() 1557 serviceState.fillInNotifierBundle(bundle) 1558 1559 return Intent(Intent.ACTION_SERVICE_STATE).apply { 1560 putExtras(bundle) 1561 putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId) 1562 } 1563 } 1564 } 1565 } 1566