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.BroadcastReceiver
21 import android.content.Context
22 import android.content.Intent
23 import android.content.IntentFilter
24 import android.net.ConnectivityManager
25 import android.net.ConnectivityManager.NetworkCallback
26 import android.net.Network
27 import android.net.NetworkCapabilities
28 import android.net.NetworkRequest
29 import android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
30 import android.telephony.CellSignalStrengthCdma
31 import android.telephony.ServiceState
32 import android.telephony.SignalStrength
33 import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
34 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
35 import android.telephony.TelephonyCallback
36 import android.telephony.TelephonyDisplayInfo
37 import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
38 import android.telephony.TelephonyManager
39 import android.telephony.TelephonyManager.ERI_FLASH
40 import android.telephony.TelephonyManager.ERI_ON
41 import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
42 import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
43 import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID
44 import com.android.settingslib.Utils
45 import com.android.systemui.broadcast.BroadcastDispatcher
46 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
47 import com.android.systemui.dagger.qualifiers.Application
48 import com.android.systemui.dagger.qualifiers.Background
49 import com.android.systemui.flags.FeatureFlagsClassic
50 import com.android.systemui.flags.Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO
51 import com.android.systemui.log.table.TableLogBuffer
52 import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
53 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Disconnected
54 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
55 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
56 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
57 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType
58 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
59 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
60 import com.android.systemui.statusbar.pipeline.mobile.data.model.toDataConnectionType
61 import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
62 import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
63 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
64 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
65 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
66 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
67 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
68 import javax.inject.Inject
69 import kotlinx.coroutines.CoroutineDispatcher
70 import kotlinx.coroutines.CoroutineScope
71 import kotlinx.coroutines.ExperimentalCoroutinesApi
72 import kotlinx.coroutines.asExecutor
73 import kotlinx.coroutines.channels.awaitClose
74 import kotlinx.coroutines.flow.Flow
75 import kotlinx.coroutines.flow.MutableStateFlow
76 import kotlinx.coroutines.flow.SharingStarted
77 import kotlinx.coroutines.flow.StateFlow
78 import kotlinx.coroutines.flow.asStateFlow
79 import kotlinx.coroutines.flow.callbackFlow
80 import kotlinx.coroutines.flow.filter
81 import kotlinx.coroutines.flow.flowOn
82 import kotlinx.coroutines.flow.map
83 import kotlinx.coroutines.flow.mapLatest
84 import kotlinx.coroutines.flow.mapNotNull
85 import kotlinx.coroutines.flow.onStart
86 import kotlinx.coroutines.flow.scan
87 import kotlinx.coroutines.flow.stateIn
88 import kotlinx.coroutines.withContext
89
90 /**
91 * A repository implementation for a typical mobile connection (as opposed to a carrier merged
92 * connection -- see [CarrierMergedConnectionRepository]).
93 */
94 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
95 @OptIn(ExperimentalCoroutinesApi::class)
96 class MobileConnectionRepositoryImpl(
97 override val subId: Int,
98 private val context: Context,
99 subscriptionModel: Flow<SubscriptionModel?>,
100 defaultNetworkName: NetworkNameModel,
101 networkNameSeparator: String,
102 connectivityManager: ConnectivityManager,
103 private val telephonyManager: TelephonyManager,
104 systemUiCarrierConfig: SystemUiCarrierConfig,
105 broadcastDispatcher: BroadcastDispatcher,
106 private val mobileMappingsProxy: MobileMappingsProxy,
107 private val bgDispatcher: CoroutineDispatcher,
108 logger: MobileInputLogger,
109 override val tableLogBuffer: TableLogBuffer,
110 flags: FeatureFlagsClassic,
111 scope: CoroutineScope,
112 ) : MobileConnectionRepository {
113 init {
114 if (telephonyManager.subscriptionId != subId) {
115 throw IllegalStateException(
116 "MobileRepo: TelephonyManager should be created with subId($subId). " +
117 "Found ${telephonyManager.subscriptionId} instead."
118 )
119 }
120 }
121
122 /**
123 * This flow defines the single shared connection to system_server via TelephonyCallback. Any
124 * new callback should be added to this listener and funneled through callbackEvents via a data
125 * class. See [CallbackEvent] for defining new callbacks.
126 *
127 * The reason we need to do this is because TelephonyManager limits the number of registered
128 * listeners per-process, so we don't want to create a new listener for every callback.
129 *
130 * A note on the design for back pressure here: We don't control _which_ telephony callback
131 * comes in first, since we register every relevant bit of information as a batch. E.g., if a
132 * downstream starts collecting on a field which is backed by
133 * [TelephonyCallback.ServiceStateListener], it's not possible for us to guarantee that _that_
134 * callback comes in -- the first callback could very well be
135 * [TelephonyCallback.DataActivityListener], which would promptly be dropped if we didn't keep
136 * it tracked. We use the [scan] operator here to track the most recent callback of _each type_
137 * here. See [TelephonyCallbackState] to see how the callbacks are stored.
138 */
139 private val callbackEvents: StateFlow<TelephonyCallbackState> = run {
140 val initial = TelephonyCallbackState()
141 callbackFlow {
142 val callback =
143 object :
144 TelephonyCallback(),
145 TelephonyCallback.CarrierNetworkListener,
146 TelephonyCallback.CarrierRoamingNtnModeListener,
147 TelephonyCallback.DataActivityListener,
148 TelephonyCallback.DataConnectionStateListener,
149 TelephonyCallback.DataEnabledListener,
150 TelephonyCallback.DisplayInfoListener,
151 TelephonyCallback.ServiceStateListener,
152 TelephonyCallback.SignalStrengthsListener {
153
154 override fun onCarrierNetworkChange(active: Boolean) {
155 logger.logOnCarrierNetworkChange(active, subId)
156 trySend(CallbackEvent.OnCarrierNetworkChange(active))
157 }
158
159 override fun onCarrierRoamingNtnModeChanged(active: Boolean) {
160 logger.logOnCarrierRoamingNtnModeChanged(active)
161 trySend(CallbackEvent.OnCarrierRoamingNtnModeChanged(active))
162 }
163
164 override fun onDataActivity(direction: Int) {
165 logger.logOnDataActivity(direction, subId)
166 trySend(CallbackEvent.OnDataActivity(direction))
167 }
168
169 override fun onDataEnabledChanged(enabled: Boolean, reason: Int) {
170 logger.logOnDataEnabledChanged(enabled, subId)
171 trySend(CallbackEvent.OnDataEnabledChanged(enabled))
172 }
173
174 override fun onDataConnectionStateChanged(
175 dataState: Int,
176 networkType: Int
177 ) {
178 logger.logOnDataConnectionStateChanged(dataState, networkType, subId)
179 trySend(CallbackEvent.OnDataConnectionStateChanged(dataState))
180 }
181
182 override fun onDisplayInfoChanged(
183 telephonyDisplayInfo: TelephonyDisplayInfo
184 ) {
185 logger.logOnDisplayInfoChanged(telephonyDisplayInfo, subId)
186 trySend(CallbackEvent.OnDisplayInfoChanged(telephonyDisplayInfo))
187 }
188
189 override fun onServiceStateChanged(serviceState: ServiceState) {
190 logger.logOnServiceStateChanged(serviceState, subId)
191 trySend(CallbackEvent.OnServiceStateChanged(serviceState))
192 }
193
194 override fun onSignalStrengthsChanged(signalStrength: SignalStrength) {
195 logger.logOnSignalStrengthsChanged(signalStrength, subId)
196 trySend(CallbackEvent.OnSignalStrengthChanged(signalStrength))
197 }
198 }
199 telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
200 awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
201 }
202 .flowOn(bgDispatcher)
203 .scan(initial = initial) { state, event -> state.applyEvent(event) }
204 .stateIn(scope = scope, started = SharingStarted.WhileSubscribed(), initial)
205 }
206
207 override val isEmergencyOnly =
208 callbackEvents
209 .mapNotNull { it.onServiceStateChanged }
210 .map { it.serviceState.isEmergencyOnly }
211 .stateIn(scope, SharingStarted.WhileSubscribed(), false)
212
213 override val isRoaming =
214 if (flags.isEnabled(ROAMING_INDICATOR_VIA_DISPLAY_INFO)) {
215 callbackEvents
216 .mapNotNull { it.onDisplayInfoChanged }
217 .map { it.telephonyDisplayInfo.isRoaming }
218 } else {
219 callbackEvents
220 .mapNotNull { it.onServiceStateChanged }
221 .map { it.serviceState.roaming }
222 }
223 .stateIn(scope, SharingStarted.WhileSubscribed(), false)
224
225 override val operatorAlphaShort =
226 callbackEvents
227 .mapNotNull { it.onServiceStateChanged }
228 .map { it.serviceState.operatorAlphaShort }
229 .stateIn(scope, SharingStarted.WhileSubscribed(), null)
230
231 override val isInService =
232 callbackEvents
233 .mapNotNull { it.onServiceStateChanged }
234 .map { Utils.isInService(it.serviceState) }
235 .stateIn(scope, SharingStarted.WhileSubscribed(), false)
236
237 override val isNonTerrestrial =
238 callbackEvents
239 .mapNotNull { it.onCarrierRoamingNtnModeChanged }
240 .map { it.active }
241 .stateIn(scope, SharingStarted.WhileSubscribed(), false)
242
243 override val isGsm =
244 callbackEvents
245 .mapNotNull { it.onSignalStrengthChanged }
246 .map { it.signalStrength.isGsm }
247 .stateIn(scope, SharingStarted.WhileSubscribed(), false)
248
249 override val cdmaLevel =
250 callbackEvents
251 .mapNotNull { it.onSignalStrengthChanged }
252 .map {
253 it.signalStrength.getCellSignalStrengths(CellSignalStrengthCdma::class.java).let {
254 strengths ->
255 if (strengths.isNotEmpty()) {
256 strengths[0].level
257 } else {
258 SIGNAL_STRENGTH_NONE_OR_UNKNOWN
259 }
260 }
261 }
262 .stateIn(scope, SharingStarted.WhileSubscribed(), SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
263
264 override val primaryLevel =
265 callbackEvents
266 .mapNotNull { it.onSignalStrengthChanged }
267 .map { it.signalStrength.level }
268 .stateIn(scope, SharingStarted.WhileSubscribed(), SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
269
270 override val dataConnectionState =
271 callbackEvents
272 .mapNotNull { it.onDataConnectionStateChanged }
273 .map { it.dataState.toDataConnectionType() }
274 .stateIn(scope, SharingStarted.WhileSubscribed(), Disconnected)
275
276 override val dataActivityDirection =
277 callbackEvents
278 .mapNotNull { it.onDataActivity }
279 .map { it.direction.toMobileDataActivityModel() }
280 .stateIn(
281 scope,
282 SharingStarted.WhileSubscribed(),
283 DataActivityModel(hasActivityIn = false, hasActivityOut = false)
284 )
285
286 override val carrierNetworkChangeActive =
287 callbackEvents
288 .mapNotNull { it.onCarrierNetworkChange }
289 .map { it.active }
290 .stateIn(scope, SharingStarted.WhileSubscribed(), false)
291
292 override val resolvedNetworkType =
293 callbackEvents
294 .mapNotNull { it.onDisplayInfoChanged }
295 .map {
296 if (it.telephonyDisplayInfo.overrideNetworkType != OVERRIDE_NETWORK_TYPE_NONE) {
297 OverrideNetworkType(
298 mobileMappingsProxy.toIconKeyOverride(
299 it.telephonyDisplayInfo.overrideNetworkType
300 )
301 )
302 } else if (it.telephonyDisplayInfo.networkType != NETWORK_TYPE_UNKNOWN) {
303 DefaultNetworkType(
304 mobileMappingsProxy.toIconKey(it.telephonyDisplayInfo.networkType)
305 )
306 } else {
307 UnknownNetworkType
308 }
309 }
310 .stateIn(scope, SharingStarted.WhileSubscribed(), UnknownNetworkType)
311
312 override val inflateSignalStrength = systemUiCarrierConfig.shouldInflateSignalStrength
313 override val allowNetworkSliceIndicator = systemUiCarrierConfig.allowNetworkSliceIndicator
314
315 override val numberOfLevels =
316 inflateSignalStrength
317 .map { shouldInflate ->
318 if (shouldInflate) {
319 DEFAULT_NUM_LEVELS + 1
320 } else {
321 DEFAULT_NUM_LEVELS
322 }
323 }
324 .stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
325
326 override val carrierName =
327 subscriptionModel
328 .map {
329 it?.let { model -> NetworkNameModel.SubscriptionDerived(model.carrierName) }
330 ?: defaultNetworkName
331 }
332 .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
333
334 /**
335 * There are a few cases where we will need to poll [TelephonyManager] so we can update some
336 * internal state where callbacks aren't provided. Any of those events should be merged into
337 * this flow, which can be used to trigger the polling.
338 */
339 private val telephonyPollingEvent: Flow<Unit> = callbackEvents.map { Unit }
340
341 override val cdmaRoaming: StateFlow<Boolean> =
342 telephonyPollingEvent
343 .mapLatest {
344 try {
345 val cdmaEri = telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber
346 cdmaEri == ERI_ON || cdmaEri == ERI_FLASH
347 } catch (e: UnsupportedOperationException) {
348 // Handles the same as a function call failure
349 false
350 }
351 }
352 .stateIn(scope, SharingStarted.WhileSubscribed(), false)
353
354 override val carrierId =
355 broadcastDispatcher
356 .broadcastFlow(
357 filter =
358 IntentFilter(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED),
359 map = { intent, _ -> intent },
360 )
361 .filter { intent ->
362 intent.getIntExtra(EXTRA_SUBSCRIPTION_ID, INVALID_SUBSCRIPTION_ID) == subId
363 }
364 .map { it.carrierId() }
365 .onStart {
366 // Make sure we get the initial carrierId
367 emit(telephonyManager.simCarrierId)
368 }
369 .stateIn(scope, SharingStarted.WhileSubscribed(), telephonyManager.simCarrierId)
370
371 /**
372 * BroadcastDispatcher does not handle sticky broadcasts, so we can't use it here. Note that we
373 * now use the [SharingStarted.Eagerly] strategy, because there have been cases where the sticky
374 * broadcast does not represent the correct state.
375 *
376 * See b/322432056 for context.
377 */
378 @SuppressLint("RegisterReceiverViaContext")
379 override val networkName: StateFlow<NetworkNameModel> =
380 conflatedCallbackFlow {
381 val receiver =
382 object : BroadcastReceiver() {
383 override fun onReceive(context: Context, intent: Intent) {
384 if (
385 intent.getIntExtra(
386 EXTRA_SUBSCRIPTION_INDEX,
387 INVALID_SUBSCRIPTION_ID
388 ) == subId
389 ) {
390 logger.logServiceProvidersUpdatedBroadcast(intent)
391 trySend(
392 intent.toNetworkNameModel(networkNameSeparator)
393 ?: defaultNetworkName
394 )
395 }
396 }
397 }
398
399 context.registerReceiver(
400 receiver,
401 IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)
402 )
403
404 awaitClose { context.unregisterReceiver(receiver) }
405 }
406 .flowOn(bgDispatcher)
407 .stateIn(scope, SharingStarted.Eagerly, defaultNetworkName)
408
409 override val dataEnabled = run {
410 val initial = telephonyManager.isDataConnectionAllowed
411 callbackEvents
412 .mapNotNull { it.onDataEnabledChanged }
413 .map { it.enabled }
414 .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
415 }
416
417 override suspend fun isInEcmMode(): Boolean =
418 withContext(bgDispatcher) { telephonyManager.emergencyCallbackMode }
419
420 /** Typical mobile connections aren't available during airplane mode. */
421 override val isAllowedDuringAirplaneMode = MutableStateFlow(false).asStateFlow()
422
423 /**
424 * Currently, a network with NET_CAPABILITY_PRIORITIZE_LATENCY is the only type of network that
425 * we consider to be a "network slice". _PRIORITIZE_BANDWIDTH may be added in the future. Any of
426 * these capabilities that are used here must also be represented in the
427 * self_certified_network_capabilities.xml config file
428 */
429 @SuppressLint("WrongConstant")
430 private val networkSliceRequest =
431 NetworkRequest.Builder()
432 .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)
433 .setSubscriptionIds(setOf(subId))
434 .build()
435
436 @SuppressLint("MissingPermission")
437 override val hasPrioritizedNetworkCapabilities: StateFlow<Boolean> =
438 conflatedCallbackFlow {
439 // Our network callback listens only for this.subId && net_cap_prioritize_latency
440 // therefore our state is a simple mapping of whether or not that network exists
441 val callback =
442 object : NetworkCallback() {
443 override fun onAvailable(network: Network) {
444 logger.logPrioritizedNetworkAvailable(network.netId)
445 trySend(true)
446 }
447
448 override fun onLost(network: Network) {
449 logger.logPrioritizedNetworkLost(network.netId)
450 trySend(false)
451 }
452 }
453
454 connectivityManager.registerNetworkCallback(networkSliceRequest, callback)
455
456 awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
457 }
458 .flowOn(bgDispatcher)
459 .stateIn(scope, SharingStarted.WhileSubscribed(), false)
460
461 class Factory
462 @Inject
463 constructor(
464 private val context: Context,
465 private val broadcastDispatcher: BroadcastDispatcher,
466 private val connectivityManager: ConnectivityManager,
467 private val telephonyManager: TelephonyManager,
468 private val logger: MobileInputLogger,
469 private val carrierConfigRepository: CarrierConfigRepository,
470 private val mobileMappingsProxy: MobileMappingsProxy,
471 private val flags: FeatureFlagsClassic,
472 @Background private val bgDispatcher: CoroutineDispatcher,
473 @Application private val scope: CoroutineScope,
474 ) {
475 fun build(
476 subId: Int,
477 mobileLogger: TableLogBuffer,
478 subscriptionModel: Flow<SubscriptionModel?>,
479 defaultNetworkName: NetworkNameModel,
480 networkNameSeparator: String,
481 ): MobileConnectionRepository {
482 return MobileConnectionRepositoryImpl(
483 subId,
484 context,
485 subscriptionModel,
486 defaultNetworkName,
487 networkNameSeparator,
488 connectivityManager,
489 telephonyManager.createForSubscriptionId(subId),
490 carrierConfigRepository.getOrCreateConfigForSubId(subId),
491 broadcastDispatcher,
492 mobileMappingsProxy,
493 bgDispatcher,
494 logger,
495 mobileLogger,
496 flags,
497 scope,
498 )
499 }
500 }
501 }
502
Intentnull503 private fun Intent.carrierId(): Int =
504 getIntExtra(TelephonyManager.EXTRA_CARRIER_ID, UNKNOWN_CARRIER_ID)
505
506 /**
507 * Wrap every [TelephonyCallback] we care about in a data class so we can accept them in a single
508 * shared flow and then split them back out into other flows.
509 */
510 sealed interface CallbackEvent {
511 data class OnCarrierNetworkChange(val active: Boolean) : CallbackEvent
512
513 data class OnCarrierRoamingNtnModeChanged(val active: Boolean) : CallbackEvent
514
515 data class OnDataActivity(val direction: Int) : CallbackEvent
516
517 data class OnDataConnectionStateChanged(val dataState: Int) : CallbackEvent
518
519 data class OnDataEnabledChanged(val enabled: Boolean) : CallbackEvent
520
521 data class OnDisplayInfoChanged(val telephonyDisplayInfo: TelephonyDisplayInfo) : CallbackEvent
522
523 data class OnServiceStateChanged(val serviceState: ServiceState) : CallbackEvent
524
525 data class OnSignalStrengthChanged(val signalStrength: SignalStrength) : CallbackEvent
526 }
527
528 /**
529 * A simple box type for 1-to-1 mapping of [CallbackEvent] to the batched event. Used in conjunction
530 * with [scan] to make sure we don't drop important callbacks due to late subscribers
531 */
532 data class TelephonyCallbackState(
533 val onDataActivity: CallbackEvent.OnDataActivity? = null,
534 val onCarrierNetworkChange: CallbackEvent.OnCarrierNetworkChange? = null,
535 val onCarrierRoamingNtnModeChanged: CallbackEvent.OnCarrierRoamingNtnModeChanged? = null,
536 val onDataConnectionStateChanged: CallbackEvent.OnDataConnectionStateChanged? = null,
537 val onDataEnabledChanged: CallbackEvent.OnDataEnabledChanged? = null,
538 val onDisplayInfoChanged: CallbackEvent.OnDisplayInfoChanged? = null,
539 val onServiceStateChanged: CallbackEvent.OnServiceStateChanged? = null,
540 val onSignalStrengthChanged: CallbackEvent.OnSignalStrengthChanged? = null,
541 ) {
applyEventnull542 fun applyEvent(event: CallbackEvent): TelephonyCallbackState {
543 return when (event) {
544 is CallbackEvent.OnCarrierNetworkChange -> copy(onCarrierNetworkChange = event)
545 is CallbackEvent.OnCarrierRoamingNtnModeChanged -> {
546 copy(onCarrierRoamingNtnModeChanged = event)
547 }
548 is CallbackEvent.OnDataActivity -> copy(onDataActivity = event)
549 is CallbackEvent.OnDataConnectionStateChanged ->
550 copy(onDataConnectionStateChanged = event)
551 is CallbackEvent.OnDataEnabledChanged -> copy(onDataEnabledChanged = event)
552 is CallbackEvent.OnDisplayInfoChanged -> copy(onDisplayInfoChanged = event)
553 is CallbackEvent.OnServiceStateChanged -> {
554 copy(onServiceStateChanged = event)
555 }
556 is CallbackEvent.OnSignalStrengthChanged -> copy(onSignalStrengthChanged = event)
557 }
558 }
559 }
560