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.connectivity.ui
18 
19 import android.content.Context
20 import android.content.res.Configuration
21 import android.os.Bundle
22 import android.telephony.SubscriptionInfo
23 import android.view.ContextThemeWrapper
24 import com.android.systemui.Dumpable
25 import com.android.systemui.dagger.SysUISingleton
26 import com.android.systemui.demomode.DemoMode
27 import com.android.systemui.demomode.DemoMode.COMMAND_NETWORK
28 import com.android.systemui.demomode.DemoModeController
29 import com.android.systemui.dump.DumpManager
30 import com.android.systemui.statusbar.connectivity.NetworkController
31 import com.android.systemui.statusbar.connectivity.SignalCallback
32 import java.io.PrintWriter
33 import javax.inject.Inject
34 
35 /**
36  * Every subscriptionId can have its own CarrierConfig associated with it, so we have to create our
37  * own [Configuration] and track resources based on the full set of available mcc-mnc combinations.
38  *
39  * (for future reference: b/240555502 is the initiating bug for this)
40  *
41  * NOTE: MCC/MNC qualifiers are not sufficient to fully describe a network type icon qualified by
42  * network type + carrier ID. This class exists to keep the legacy behavior of using the MCC/MNC
43  * resource qualifiers working, but if a carrier-specific icon is requested, then the override
44  * provided by [MobileIconCarrierIdOverrides] will take precedence.
45  *
46  * TODO(b/258503704): consider removing this class in favor of the `carrierId` overrides
47  */
48 @SysUISingleton
49 class MobileContextProvider
50 @Inject
51 constructor(
52     networkController: NetworkController,
53     dumpManager: DumpManager,
54     private val demoModeController: DemoModeController,
55 ) : Dumpable, DemoMode {
56     private val subscriptions = mutableMapOf<Int, SubscriptionInfo>()
57     private val signalCallback =
58         object : SignalCallback {
59             override fun setSubs(subs: List<SubscriptionInfo>) {
60                 subscriptions.clear()
61                 subs.forEach { info -> subscriptions[info.subscriptionId] = info }
62             }
63         }
64 
65     // These should always be null when not in demo mode
66     private var demoMcc: Int? = null
67     private var demoMnc: Int? = null
68 
69     init {
70         networkController.addCallback(signalCallback)
71         dumpManager.registerDumpable(this)
72         demoModeController.addCallback(this)
73     }
74 
75     /**
76      * @return a context with the MCC/MNC [Configuration] values corresponding to this
77      *   subscriptionId
78      */
79     fun getMobileContextForSub(subId: Int, context: Context): Context {
80         if (demoModeController.isInDemoMode) {
81             return createMobileContextForDemoMode(context)
82         }
83 
84         // Fail back to the given context if no sub exists
85         val info = subscriptions[subId] ?: return context
86 
87         return createCarrierConfigContext(context, info.mcc, info.mnc)
88     }
89 
90     /** For Demo mode (for now), just apply the same MCC/MNC override for all subIds */
91     private fun createMobileContextForDemoMode(context: Context): Context {
92         return createCarrierConfigContext(context, demoMcc ?: 0, demoMnc ?: 0)
93     }
94 
95     override fun dump(pw: PrintWriter, args: Array<out String>) {
96         pw.println(
97             "Subscriptions below will be inflated with a configuration context with " +
98                 "MCC/MNC overrides"
99         )
100         subscriptions.forEach { (subId, info) ->
101             pw.println("  Subscription with subId($subId) with MCC/MNC(${info.mcc}/${info.mnc})")
102         }
103         pw.println("  MCC override: ${demoMcc ?: "(none)"}")
104         pw.println("  MNC override: ${demoMnc ?: "(none)"}")
105     }
106 
107     override fun demoCommands(): List<String> {
108         return listOf(COMMAND_NETWORK)
109     }
110 
111     override fun onDemoModeFinished() {
112         demoMcc = null
113         demoMnc = null
114     }
115 
116     override fun dispatchDemoCommand(command: String, args: Bundle) {
117         val mccmnc = args.getString("mccmnc") ?: return
118         // Only length 5/6 strings are valid
119         if (!(mccmnc.length == 5 || mccmnc.length == 6)) {
120             return
121         }
122 
123         // MCC is always the first 3 digits, and mnc is the last 2 or 3
124         demoMcc = mccmnc.subSequence(0, 3).toString().toInt()
125         demoMnc = mccmnc.subSequence(3, mccmnc.length).toString().toInt()
126     }
127 
128     companion object {
129         /**
130          * Creates a context based on this [SubscriptionInfo]'s MCC/MNC values, allowing the overlay
131          * system to properly load different carrier's iconography
132          */
133         private fun createCarrierConfigContext(context: Context, mcc: Int, mnc: Int): Context {
134             // Copy the existing configuration
135             val c = Configuration(context.resources.configuration)
136             c.mcc = mcc
137             c.mnc = mnc
138 
139             return ContextThemeWrapper(context, context.theme).also { ctx ->
140                 ctx.applyOverrideConfiguration(c)
141             }
142         }
143     }
144 }
145