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