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.systemui.statusbar.pipeline.mobile.data.repository.prod
18 
19 import android.net.ConnectivityManager
20 import android.os.PersistableBundle
21 import android.telephony.ServiceState
22 import android.telephony.SignalStrength
23 import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
24 import android.telephony.TelephonyCallback
25 import android.telephony.TelephonyManager
26 import androidx.test.ext.junit.runners.AndroidJUnit4
27 import androidx.test.filters.SmallTest
28 import com.android.systemui.SysuiTestCase
29 import com.android.systemui.coroutines.collectLastValue
30 import com.android.systemui.flags.FakeFeatureFlagsClassic
31 import com.android.systemui.flags.Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO
32 import com.android.systemui.log.table.logcatTableLogBuffer
33 import com.android.systemui.log.table.tableLogBufferFactory
34 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
35 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
36 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
37 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
38 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
39 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_EMERGENCY
40 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_OPERATOR
41 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_PRIMARY_LEVEL
42 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.getTelephonyCallbackForType
43 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
44 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
45 import com.android.systemui.testKosmos
46 import com.android.systemui.util.mockito.any
47 import com.android.systemui.util.mockito.eq
48 import com.android.systemui.util.mockito.mock
49 import com.android.systemui.util.mockito.whenever
50 import com.google.common.truth.Truth.assertThat
51 import java.io.PrintWriter
52 import java.io.StringWriter
53 import kotlinx.coroutines.ExperimentalCoroutinesApi
54 import kotlinx.coroutines.flow.MutableStateFlow
55 import kotlinx.coroutines.flow.launchIn
56 import kotlinx.coroutines.flow.onEach
57 import kotlinx.coroutines.test.TestScope
58 import kotlinx.coroutines.test.UnconfinedTestDispatcher
59 import kotlinx.coroutines.test.runTest
60 import org.junit.Before
61 import org.junit.Test
62 import org.junit.runner.RunWith
63 import org.mockito.Mockito.never
64 import org.mockito.Mockito.verify
65 
66 /**
67  * This repo acts as a dispatcher to either the `typical` or `carrier merged` versions of the
68  * repository interface it's switching on. These tests just need to verify that the entire interface
69  * properly switches over when the value of `isCarrierMerged` changes.
70  */
71 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
72 @OptIn(ExperimentalCoroutinesApi::class)
73 @SmallTest
74 @RunWith(AndroidJUnit4::class)
75 class FullMobileConnectionRepositoryTest : SysuiTestCase() {
76     private val kosmos = testKosmos()
77 
78     private lateinit var underTest: FullMobileConnectionRepository
79 
80     private val flags =
<lambda>null81         FakeFeatureFlagsClassic().also { it.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
82 
83     private val testDispatcher = UnconfinedTestDispatcher()
84     private val testScope = TestScope(testDispatcher)
85     private val tableLogBuffer = logcatTableLogBuffer(kosmos, "TestName")
86     private val mobileFactory = mock<MobileConnectionRepositoryImpl.Factory>()
87     private val carrierMergedFactory = mock<CarrierMergedConnectionRepository.Factory>()
88     private val connectivityManager = mock<ConnectivityManager>()
89 
90     private val subscriptionModel =
91         MutableStateFlow(
92             SubscriptionModel(
93                 subscriptionId = SUB_ID,
94                 carrierName = DEFAULT_NAME,
95                 profileClass = PROFILE_CLASS_UNSET,
96             )
97         )
98 
99     // Use a real config, with no overrides
100     private val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_ID, PersistableBundle())
101 
102     private lateinit var mobileRepo: FakeMobileConnectionRepository
103     private lateinit var carrierMergedRepo: FakeMobileConnectionRepository
104 
105     @Before
setUpnull106     fun setUp() {
107         mobileRepo =
108             FakeMobileConnectionRepository(
109                 SUB_ID,
110                 tableLogBuffer,
111             )
112         carrierMergedRepo =
113             FakeMobileConnectionRepository(
114                     SUB_ID,
115                     tableLogBuffer,
116                 )
117                 .apply {
118                     // Mimicks the real carrier merged repository
119                     this.isAllowedDuringAirplaneMode.value = true
120                 }
121 
122         whenever(
123                 mobileFactory.build(
124                     eq(SUB_ID),
125                     any(),
126                     any(),
127                     eq(DEFAULT_NAME_MODEL),
128                     eq(SEP),
129                 )
130             )
131             .thenReturn(mobileRepo)
132         whenever(
133                 carrierMergedFactory.build(
134                     eq(SUB_ID),
135                     any(),
136                 )
137             )
138             .thenReturn(carrierMergedRepo)
139     }
140 
141     @Test
startingIsCarrierMerged_usesCarrierMergedInitiallynull142     fun startingIsCarrierMerged_usesCarrierMergedInitially() =
143         testScope.runTest {
144             val carrierMergedOperatorName = "Carrier Merged Operator"
145             val nonCarrierMergedName = "Non-carrier-merged"
146 
147             carrierMergedRepo.operatorAlphaShort.value = carrierMergedOperatorName
148             mobileRepo.operatorAlphaShort.value = nonCarrierMergedName
149 
150             initializeRepo(startingIsCarrierMerged = true)
151 
152             assertThat(underTest.activeRepo.value).isEqualTo(carrierMergedRepo)
153             assertThat(underTest.operatorAlphaShort.value).isEqualTo(carrierMergedOperatorName)
154             verify(mobileFactory, never())
155                 .build(
156                     SUB_ID,
157                     tableLogBuffer,
158                     subscriptionModel,
159                     DEFAULT_NAME_MODEL,
160                     SEP,
161                 )
162         }
163 
164     @Test
startingNotCarrierMerged_usesTypicalInitiallynull165     fun startingNotCarrierMerged_usesTypicalInitially() =
166         testScope.runTest {
167             val carrierMergedOperatorName = "Carrier Merged Operator"
168             val nonCarrierMergedName = "Typical Operator"
169 
170             carrierMergedRepo.operatorAlphaShort.value = carrierMergedOperatorName
171             mobileRepo.operatorAlphaShort.value = nonCarrierMergedName
172 
173             initializeRepo(startingIsCarrierMerged = false)
174 
175             assertThat(underTest.activeRepo.value).isEqualTo(mobileRepo)
176             assertThat(underTest.operatorAlphaShort.value).isEqualTo(nonCarrierMergedName)
177             verify(carrierMergedFactory, never())
178                 .build(
179                     SUB_ID,
180                     tableLogBuffer,
181                 )
182         }
183 
184     @Test
activeRepo_matchesIsCarrierMergednull185     fun activeRepo_matchesIsCarrierMerged() =
186         testScope.runTest {
187             initializeRepo(startingIsCarrierMerged = false)
188             var latest: MobileConnectionRepository? = null
189             val job = underTest.activeRepo.onEach { latest = it }.launchIn(this)
190 
191             underTest.setIsCarrierMerged(true)
192 
193             assertThat(latest).isEqualTo(carrierMergedRepo)
194 
195             underTest.setIsCarrierMerged(false)
196 
197             assertThat(latest).isEqualTo(mobileRepo)
198 
199             underTest.setIsCarrierMerged(true)
200 
201             assertThat(latest).isEqualTo(carrierMergedRepo)
202 
203             job.cancel()
204         }
205 
206     @Test
connectionInfo_getsUpdatesFromRepo_carrierMergednull207     fun connectionInfo_getsUpdatesFromRepo_carrierMerged() =
208         testScope.runTest {
209             initializeRepo(startingIsCarrierMerged = false)
210 
211             var latestName: String? = null
212             var latestLevel: Int? = null
213 
214             val nameJob = underTest.operatorAlphaShort.onEach { latestName = it }.launchIn(this)
215             val levelJob = underTest.primaryLevel.onEach { latestLevel = it }.launchIn(this)
216 
217             underTest.setIsCarrierMerged(true)
218 
219             val operator1 = "Carrier Merged Operator"
220             val level1 = 1
221             carrierMergedRepo.operatorAlphaShort.value = operator1
222             carrierMergedRepo.primaryLevel.value = level1
223 
224             assertThat(latestName).isEqualTo(operator1)
225             assertThat(latestLevel).isEqualTo(level1)
226 
227             val operator2 = "Carrier Merged Operator #2"
228             val level2 = 2
229             carrierMergedRepo.operatorAlphaShort.value = operator2
230             carrierMergedRepo.primaryLevel.value = level2
231 
232             assertThat(latestName).isEqualTo(operator2)
233             assertThat(latestLevel).isEqualTo(level2)
234 
235             val operator3 = "Carrier Merged Operator #3"
236             val level3 = 3
237             carrierMergedRepo.operatorAlphaShort.value = operator3
238             carrierMergedRepo.primaryLevel.value = level3
239 
240             assertThat(latestName).isEqualTo(operator3)
241             assertThat(latestLevel).isEqualTo(level3)
242 
243             nameJob.cancel()
244             levelJob.cancel()
245         }
246 
247     @Test
connectionInfo_getsUpdatesFromRepo_mobilenull248     fun connectionInfo_getsUpdatesFromRepo_mobile() =
249         testScope.runTest {
250             initializeRepo(startingIsCarrierMerged = false)
251 
252             var latestName: String? = null
253             var latestLevel: Int? = null
254 
255             val nameJob = underTest.operatorAlphaShort.onEach { latestName = it }.launchIn(this)
256             val levelJob = underTest.primaryLevel.onEach { latestLevel = it }.launchIn(this)
257 
258             underTest.setIsCarrierMerged(false)
259 
260             val operator1 = "Typical Merged Operator"
261             val level1 = 1
262             mobileRepo.operatorAlphaShort.value = operator1
263             mobileRepo.primaryLevel.value = level1
264 
265             assertThat(latestName).isEqualTo(operator1)
266             assertThat(latestLevel).isEqualTo(level1)
267 
268             val operator2 = "Typical Merged Operator #2"
269             val level2 = 2
270             mobileRepo.operatorAlphaShort.value = operator2
271             mobileRepo.primaryLevel.value = level2
272 
273             assertThat(latestName).isEqualTo(operator2)
274             assertThat(latestLevel).isEqualTo(level2)
275 
276             val operator3 = "Typical Merged Operator #3"
277             val level3 = 3
278             mobileRepo.operatorAlphaShort.value = operator3
279             mobileRepo.primaryLevel.value = level3
280 
281             assertThat(latestName).isEqualTo(operator3)
282             assertThat(latestLevel).isEqualTo(level3)
283 
284             nameJob.cancel()
285             levelJob.cancel()
286         }
287 
288     @Test
connectionInfo_updatesWhenCarrierMergedUpdatesnull289     fun connectionInfo_updatesWhenCarrierMergedUpdates() =
290         testScope.runTest {
291             initializeRepo(startingIsCarrierMerged = false)
292 
293             var latestName: String? = null
294             var latestLevel: Int? = null
295 
296             val nameJob = underTest.operatorAlphaShort.onEach { latestName = it }.launchIn(this)
297             val levelJob = underTest.primaryLevel.onEach { latestLevel = it }.launchIn(this)
298 
299             val carrierMergedOperator = "Carrier Merged Operator"
300             val carrierMergedLevel = 4
301             carrierMergedRepo.operatorAlphaShort.value = carrierMergedOperator
302             carrierMergedRepo.primaryLevel.value = carrierMergedLevel
303 
304             val mobileName = "Typical Operator"
305             val mobileLevel = 2
306             mobileRepo.operatorAlphaShort.value = mobileName
307             mobileRepo.primaryLevel.value = mobileLevel
308 
309             // Start with the mobile info
310             assertThat(latestName).isEqualTo(mobileName)
311             assertThat(latestLevel).isEqualTo(mobileLevel)
312 
313             // WHEN isCarrierMerged is set to true
314             underTest.setIsCarrierMerged(true)
315 
316             // THEN the carrier merged info is used
317             assertThat(latestName).isEqualTo(carrierMergedOperator)
318             assertThat(latestLevel).isEqualTo(carrierMergedLevel)
319 
320             val newCarrierMergedName = "New CM Operator"
321             val newCarrierMergedLevel = 0
322             carrierMergedRepo.operatorAlphaShort.value = newCarrierMergedName
323             carrierMergedRepo.primaryLevel.value = newCarrierMergedLevel
324 
325             assertThat(latestName).isEqualTo(newCarrierMergedName)
326             assertThat(latestLevel).isEqualTo(newCarrierMergedLevel)
327 
328             // WHEN isCarrierMerged is set to false
329             underTest.setIsCarrierMerged(false)
330 
331             // THEN the typical info is used
332             assertThat(latestName).isEqualTo(mobileName)
333             assertThat(latestLevel).isEqualTo(mobileLevel)
334 
335             val newMobileName = "New MobileOperator"
336             val newMobileLevel = 3
337             mobileRepo.operatorAlphaShort.value = newMobileName
338             mobileRepo.primaryLevel.value = newMobileLevel
339 
340             assertThat(latestName).isEqualTo(newMobileName)
341             assertThat(latestLevel).isEqualTo(newMobileLevel)
342 
343             nameJob.cancel()
344             levelJob.cancel()
345         }
346 
347     @Test
isAllowedDuringAirplaneMode_updatesWhenCarrierMergedUpdatesnull348     fun isAllowedDuringAirplaneMode_updatesWhenCarrierMergedUpdates() =
349         testScope.runTest {
350             initializeRepo(startingIsCarrierMerged = false)
351 
352             val latest by collectLastValue(underTest.isAllowedDuringAirplaneMode)
353 
354             assertThat(latest).isFalse()
355 
356             underTest.setIsCarrierMerged(true)
357 
358             assertThat(latest).isTrue()
359 
360             underTest.setIsCarrierMerged(false)
361 
362             assertThat(latest).isFalse()
363         }
364 
365     @Test
factory_reusesLogBuffersForSameConnectionnull366     fun factory_reusesLogBuffersForSameConnection() =
367         testScope.runTest {
368             val factory =
369                 FullMobileConnectionRepository.Factory(
370                     scope = testScope.backgroundScope,
371                     kosmos.tableLogBufferFactory,
372                     mobileFactory,
373                     carrierMergedFactory,
374                 )
375 
376             // Create two connections for the same subId. Similar to if the connection appeared
377             // and disappeared from the connectionFactory's perspective
378             val connection1 =
379                 factory.build(
380                     SUB_ID,
381                     startingIsCarrierMerged = false,
382                     subscriptionModel,
383                     DEFAULT_NAME_MODEL,
384                     SEP,
385                 )
386 
387             val connection1Repeat =
388                 factory.build(
389                     SUB_ID,
390                     startingIsCarrierMerged = false,
391                     subscriptionModel,
392                     DEFAULT_NAME_MODEL,
393                     SEP,
394                 )
395 
396             assertThat(connection1.tableLogBuffer)
397                 .isSameInstanceAs(connection1Repeat.tableLogBuffer)
398         }
399 
400     @Test
factory_reusesLogBuffersForSameSubIDevenIfCarrierMergednull401     fun factory_reusesLogBuffersForSameSubIDevenIfCarrierMerged() =
402         testScope.runTest {
403             val factory =
404                 FullMobileConnectionRepository.Factory(
405                     scope = testScope.backgroundScope,
406                     kosmos.tableLogBufferFactory,
407                     mobileFactory,
408                     carrierMergedFactory,
409                 )
410 
411             val connection1 =
412                 factory.build(
413                     SUB_ID,
414                     startingIsCarrierMerged = false,
415                     subscriptionModel,
416                     DEFAULT_NAME_MODEL,
417                     SEP,
418                 )
419 
420             // WHEN a connection with the same sub ID but carrierMerged = true is created
421             val connection1Repeat =
422                 factory.build(
423                     SUB_ID,
424                     startingIsCarrierMerged = true,
425                     subscriptionModel,
426                     DEFAULT_NAME_MODEL,
427                     SEP,
428                 )
429 
430             // THEN the same table is re-used
431             assertThat(connection1.tableLogBuffer)
432                 .isSameInstanceAs(connection1Repeat.tableLogBuffer)
433         }
434 
435     @Test
connectionInfo_logging_notCarrierMerged_getsUpdatesnull436     fun connectionInfo_logging_notCarrierMerged_getsUpdates() =
437         testScope.runTest {
438             // SETUP: Use real repositories to verify the diffing still works. (See b/267501739.)
439             val telephonyManager =
440                 mock<TelephonyManager>().apply { whenever(this.simOperatorName).thenReturn("") }
441             createRealMobileRepo(telephonyManager)
442             createRealCarrierMergedRepo(telephonyManager, FakeWifiRepository())
443 
444             initializeRepo(startingIsCarrierMerged = false)
445 
446             val emergencyJob = underTest.isEmergencyOnly.launchIn(this)
447             val operatorJob = underTest.operatorAlphaShort.launchIn(this)
448 
449             // WHEN we set up some mobile connection info
450             val serviceState = ServiceState()
451             serviceState.setOperatorName("longName", "OpTypical", "1")
452             serviceState.isEmergencyOnly = true
453             getTelephonyCallbackForType<TelephonyCallback.ServiceStateListener>(telephonyManager)
454                 .onServiceStateChanged(serviceState)
455 
456             // THEN it's logged to the buffer
457             assertThat(dumpBuffer()).contains("$COL_OPERATOR${BUFFER_SEPARATOR}OpTypical")
458             assertThat(dumpBuffer()).contains("$COL_EMERGENCY${BUFFER_SEPARATOR}true")
459 
460             // WHEN we update mobile connection info
461             val serviceState2 = ServiceState()
462             serviceState2.setOperatorName("longName", "OpDiff", "1")
463             serviceState2.isEmergencyOnly = false
464             getTelephonyCallbackForType<TelephonyCallback.ServiceStateListener>(telephonyManager)
465                 .onServiceStateChanged(serviceState2)
466 
467             // THEN the updates are logged
468             assertThat(dumpBuffer()).contains("$COL_OPERATOR${BUFFER_SEPARATOR}OpDiff")
469             assertThat(dumpBuffer()).contains("$COL_EMERGENCY${BUFFER_SEPARATOR}false")
470 
471             emergencyJob.cancel()
472             operatorJob.cancel()
473         }
474 
475     @Test
connectionInfo_logging_carrierMerged_getsUpdatesnull476     fun connectionInfo_logging_carrierMerged_getsUpdates() =
477         testScope.runTest {
478             // SETUP: Use real repositories to verify the diffing still works. (See b/267501739.)
479             val telephonyManager =
480                 mock<TelephonyManager>().apply { whenever(this.simOperatorName).thenReturn("") }
481             createRealMobileRepo(telephonyManager)
482             val wifiRepository = FakeWifiRepository()
483             createRealCarrierMergedRepo(telephonyManager, wifiRepository)
484 
485             initializeRepo(startingIsCarrierMerged = true)
486 
487             val job = underTest.primaryLevel.launchIn(this)
488 
489             // WHEN we set up carrier merged info
490             wifiRepository.setWifiNetwork(
491                 WifiNetworkModel.CarrierMerged.of(
492                     SUB_ID,
493                     level = 3,
494                 )
495             )
496 
497             // THEN the carrier merged info is logged
498             assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}3")
499 
500             // WHEN we update the info
501             wifiRepository.setWifiNetwork(
502                 WifiNetworkModel.CarrierMerged.of(
503                     SUB_ID,
504                     level = 1,
505                 )
506             )
507 
508             // THEN the updates are logged
509             assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}1")
510 
511             job.cancel()
512         }
513 
514     @Test
connectionInfo_logging_updatesWhenCarrierMergedUpdatesnull515     fun connectionInfo_logging_updatesWhenCarrierMergedUpdates() =
516         testScope.runTest {
517             // SETUP: Use real repositories to verify the diffing still works. (See b/267501739.)
518             val telephonyManager =
519                 mock<TelephonyManager>().apply { whenever(this.simOperatorName).thenReturn("") }
520             createRealMobileRepo(telephonyManager)
521 
522             val wifiRepository = FakeWifiRepository()
523             createRealCarrierMergedRepo(telephonyManager, wifiRepository)
524 
525             initializeRepo(startingIsCarrierMerged = false)
526 
527             val job = underTest.primaryLevel.launchIn(this)
528 
529             // WHEN we set up some mobile connection info
530             val signalStrength = mock<SignalStrength>()
531             whenever(signalStrength.level).thenReturn(1)
532 
533             getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>(telephonyManager)
534                 .onSignalStrengthsChanged(signalStrength)
535 
536             // THEN it's logged to the buffer
537             assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}1")
538 
539             // WHEN isCarrierMerged is set to true
540             wifiRepository.setWifiNetwork(
541                 WifiNetworkModel.CarrierMerged.of(
542                     SUB_ID,
543                     level = 3,
544                 )
545             )
546             underTest.setIsCarrierMerged(true)
547 
548             // THEN the carrier merged info is logged
549             assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}3")
550 
551             // WHEN the carrier merge network is updated
552             wifiRepository.setWifiNetwork(
553                 WifiNetworkModel.CarrierMerged.of(
554                     SUB_ID,
555                     level = 4,
556                 )
557             )
558 
559             // THEN the new level is logged
560             assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}4")
561 
562             // WHEN isCarrierMerged is set to false
563             underTest.setIsCarrierMerged(false)
564 
565             // THEN the typical info is logged
566             // Note: Since our first logs also had the typical info, we need to search the log
567             // contents for after our carrier merged level log.
568             val fullBuffer = dumpBuffer()
569             val carrierMergedContentIndex = fullBuffer.indexOf("${BUFFER_SEPARATOR}4")
570             val bufferAfterCarrierMerged = fullBuffer.substring(carrierMergedContentIndex)
571             assertThat(bufferAfterCarrierMerged).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}1")
572 
573             // WHEN the normal network is updated
574             mobileRepo.primaryLevel.value = 0
575 
576             // THEN the new level is logged
577             assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}0")
578 
579             job.cancel()
580         }
581 
582     @Test
connectionInfo_logging_doesNotLogUpdatesForNotActiveReponull583     fun connectionInfo_logging_doesNotLogUpdatesForNotActiveRepo() =
584         testScope.runTest {
585             // SETUP: Use real repositories to verify the diffing still works. (See b/267501739.)
586             val telephonyManager =
587                 mock<TelephonyManager>().apply { whenever(this.simOperatorName).thenReturn("") }
588             createRealMobileRepo(telephonyManager)
589 
590             val wifiRepository = FakeWifiRepository()
591             createRealCarrierMergedRepo(telephonyManager, wifiRepository)
592 
593             // WHEN isCarrierMerged = false
594             initializeRepo(startingIsCarrierMerged = false)
595 
596             val job = underTest.primaryLevel.launchIn(this)
597 
598             val signalStrength = mock<SignalStrength>()
599             whenever(signalStrength.level).thenReturn(1)
600             getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>(telephonyManager)
601                 .onSignalStrengthsChanged(signalStrength)
602 
603             // THEN updates to the carrier merged level aren't logged
604             wifiRepository.setWifiNetwork(
605                 WifiNetworkModel.CarrierMerged.of(
606                     SUB_ID,
607                     level = 4,
608                 )
609             )
610             assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}4")
611 
612             wifiRepository.setWifiNetwork(
613                 WifiNetworkModel.CarrierMerged.of(
614                     SUB_ID,
615                     level = 3,
616                 )
617             )
618             assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}3")
619 
620             // WHEN isCarrierMerged is set to true
621             underTest.setIsCarrierMerged(true)
622 
623             // THEN updates to the normal level aren't logged
624             whenever(signalStrength.level).thenReturn(5)
625             getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>(telephonyManager)
626                 .onSignalStrengthsChanged(signalStrength)
627             assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}5")
628 
629             whenever(signalStrength.level).thenReturn(6)
630             getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>(telephonyManager)
631                 .onSignalStrengthsChanged(signalStrength)
632             assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}6")
633 
634             job.cancel()
635         }
636 
initializeReponull637     private fun initializeRepo(startingIsCarrierMerged: Boolean) {
638         underTest =
639             FullMobileConnectionRepository(
640                 SUB_ID,
641                 startingIsCarrierMerged,
642                 tableLogBuffer,
643                 subscriptionModel,
644                 DEFAULT_NAME_MODEL,
645                 SEP,
646                 testScope.backgroundScope,
647                 mobileFactory,
648                 carrierMergedFactory,
649             )
650     }
651 
createRealMobileReponull652     private fun createRealMobileRepo(
653         telephonyManager: TelephonyManager,
654     ): MobileConnectionRepositoryImpl {
655         whenever(telephonyManager.subscriptionId).thenReturn(SUB_ID)
656         val realRepo =
657             MobileConnectionRepositoryImpl(
658                 SUB_ID,
659                 context,
660                 subscriptionModel,
661                 DEFAULT_NAME_MODEL,
662                 SEP,
663                 connectivityManager,
664                 telephonyManager,
665                 systemUiCarrierConfig = systemUiCarrierConfig,
666                 fakeBroadcastDispatcher,
667                 mobileMappingsProxy = mock(),
668                 testDispatcher,
669                 logger = mock(),
670                 tableLogBuffer,
671                 flags,
672                 testScope.backgroundScope,
673             )
674         whenever(
675                 mobileFactory.build(
676                     eq(SUB_ID),
677                     any(),
678                     any(),
679                     eq(DEFAULT_NAME_MODEL),
680                     eq(SEP),
681                 )
682             )
683             .thenReturn(realRepo)
684 
685         return realRepo
686     }
687 
createRealCarrierMergedReponull688     private fun createRealCarrierMergedRepo(
689         telephonyManager: TelephonyManager,
690         wifiRepository: FakeWifiRepository,
691     ): CarrierMergedConnectionRepository {
692         wifiRepository.setIsWifiEnabled(true)
693         wifiRepository.setIsWifiDefault(true)
694         val realRepo =
695             CarrierMergedConnectionRepository(
696                 SUB_ID,
697                 tableLogBuffer,
698                 telephonyManager,
699                 testScope.backgroundScope.coroutineContext,
700                 testScope.backgroundScope,
701                 wifiRepository,
702             )
703         whenever(
704                 carrierMergedFactory.build(
705                     eq(SUB_ID),
706                     any(),
707                 )
708             )
709             .thenReturn(realRepo)
710 
711         return realRepo
712     }
713 
dumpBuffernull714     private fun dumpBuffer(): String {
715         val outputWriter = StringWriter()
716         tableLogBuffer.dump(PrintWriter(outputWriter), arrayOf())
717         return outputWriter.toString()
718     }
719 
720     private companion object {
721         const val SUB_ID = 42
722         private const val DEFAULT_NAME = "default name"
723         private val DEFAULT_NAME_MODEL = NetworkNameModel.Default(DEFAULT_NAME)
724         private const val SEP = "-"
725         private const val BUFFER_SEPARATOR = "|"
726     }
727 }
728