1 /*
<lambda>null2  * 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
18 
19 import android.content.IntentFilter
20 import android.os.PersistableBundle
21 import android.telephony.CarrierConfigManager
22 import android.telephony.SubscriptionManager
23 import android.util.SparseArray
24 import androidx.annotation.VisibleForTesting
25 import androidx.core.util.getOrElse
26 import androidx.core.util.isEmpty
27 import androidx.core.util.keyIterator
28 import com.android.systemui.Dumpable
29 import com.android.systemui.broadcast.BroadcastDispatcher
30 import com.android.systemui.dagger.SysUISingleton
31 import com.android.systemui.dagger.qualifiers.Application
32 import com.android.systemui.dump.DumpManager
33 import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
34 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
35 import java.io.PrintWriter
36 import javax.inject.Inject
37 import kotlinx.coroutines.CoroutineScope
38 import kotlinx.coroutines.flow.SharedFlow
39 import kotlinx.coroutines.flow.SharingStarted
40 import kotlinx.coroutines.flow.filter
41 import kotlinx.coroutines.flow.mapNotNull
42 import kotlinx.coroutines.flow.onEach
43 import kotlinx.coroutines.flow.shareIn
44 
45 /**
46  * Meant to be the source of truth regarding CarrierConfigs. These are configuration objects defined
47  * on a per-subscriptionId basis, and do not trigger a device configuration event.
48  *
49  * Designed to supplant [com.android.systemui.util.CarrierConfigTracker].
50  *
51  * See [SystemUiCarrierConfig] for details on how to add carrier config keys to be tracked
52  */
53 @SysUISingleton
54 class CarrierConfigRepository
55 @Inject
56 constructor(
57     broadcastDispatcher: BroadcastDispatcher,
58     private val carrierConfigManager: CarrierConfigManager?,
59     dumpManager: DumpManager,
60     logger: MobileInputLogger,
61     @Application scope: CoroutineScope,
62 ) : Dumpable {
63     private var isListening = false
64     private val defaultConfig: PersistableBundle by lazy { CarrierConfigManager.getDefaultConfig() }
65     // Used for logging the default config in the dumpsys
66     private val defaultConfigForLogs: SystemUiCarrierConfig by lazy {
67         SystemUiCarrierConfig(-1, defaultConfig)
68     }
69 
70     private val configs = SparseArray<SystemUiCarrierConfig>()
71 
72     init {
73         dumpManager.registerNormalDumpable(this)
74     }
75 
76     @VisibleForTesting
77     val carrierConfigStream: SharedFlow<Pair<Int, PersistableBundle>> =
78         broadcastDispatcher
79             .broadcastFlow(IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
80                 intent,
81                 _ ->
82                 intent.getIntExtra(
83                     CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
84                     SubscriptionManager.INVALID_SUBSCRIPTION_ID
85                 )
86             }
87             .onEach { logger.logCarrierConfigChanged(it) }
88             .filter { SubscriptionManager.isValidSubscriptionId(it) }
89             .mapNotNull { subId ->
90                 val config = carrierConfigManager?.getConfigForSubId(subId)
91                 config?.let { subId to it }
92             }
93             .shareIn(scope, SharingStarted.WhileSubscribed())
94 
95     /**
96      * Start this repository observing broadcasts for **all** carrier configuration updates. Must be
97      * called in order to keep SystemUI in sync with [CarrierConfigManager].
98      */
99     suspend fun startObservingCarrierConfigUpdates() {
100         isListening = true
101         carrierConfigStream.collect { updateCarrierConfig(it.first, it.second) }
102     }
103 
104     /** Update or create the [SystemUiCarrierConfig] for subId with the override */
105     private fun updateCarrierConfig(subId: Int, config: PersistableBundle) {
106         val configToUpdate = getOrCreateConfigForSubId(subId)
107         configToUpdate.processNewCarrierConfig(config)
108     }
109 
110     /** Gets a cached [SystemUiCarrierConfig], or creates a new one which will track the defaults */
111     fun getOrCreateConfigForSubId(subId: Int): SystemUiCarrierConfig {
112         return configs.getOrElse(subId) {
113             val config = SystemUiCarrierConfig(subId, defaultConfig)
114             val carrierConfig = carrierConfigManager?.getConfigForSubId(subId)
115             if (carrierConfig != null) config.processNewCarrierConfig(carrierConfig)
116             configs.put(subId, config)
117             config
118         }
119     }
120 
121     override fun dump(pw: PrintWriter, args: Array<out String>) {
122         pw.println("isListening: $isListening")
123         if (configs.isEmpty()) {
124             pw.println("no carrier configs loaded")
125         } else {
126             pw.println("Carrier configs by subId")
127             configs.keyIterator().forEach {
128                 pw.println("  subId=$it")
129                 pw.println("    config=${configs.get(it).toStringConsideringDefaults()}")
130             }
131             // Finally, print the default config
132             pw.println("Default config:")
133             pw.println("  $defaultConfigForLogs")
134         }
135     }
136 }
137