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.bouncer.data.repository 18 19 import android.annotation.SuppressLint 20 import android.content.IntentFilter 21 import android.content.res.Resources 22 import android.telephony.SubscriptionInfo 23 import android.telephony.SubscriptionManager 24 import android.telephony.TelephonyManager 25 import android.telephony.euicc.EuiccManager 26 import com.android.keyguard.KeyguardUpdateMonitor 27 import com.android.keyguard.KeyguardUpdateMonitorCallback 28 import com.android.systemui.bouncer.data.model.SimBouncerModel 29 import com.android.systemui.bouncer.data.model.SimPukInputModel 30 import com.android.systemui.broadcast.BroadcastDispatcher 31 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 32 import com.android.systemui.dagger.SysUISingleton 33 import com.android.systemui.dagger.qualifiers.Application 34 import com.android.systemui.dagger.qualifiers.Background 35 import com.android.systemui.dagger.qualifiers.Main 36 import com.android.systemui.res.R 37 import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy 38 import javax.inject.Inject 39 import kotlinx.coroutines.CoroutineDispatcher 40 import kotlinx.coroutines.CoroutineScope 41 import kotlinx.coroutines.channels.awaitClose 42 import kotlinx.coroutines.flow.Flow 43 import kotlinx.coroutines.flow.MutableStateFlow 44 import kotlinx.coroutines.flow.SharingStarted 45 import kotlinx.coroutines.flow.StateFlow 46 import kotlinx.coroutines.flow.map 47 import kotlinx.coroutines.flow.merge 48 import kotlinx.coroutines.flow.stateIn 49 import kotlinx.coroutines.withContext 50 51 /** Handles data layer logic for locked sim cards. */ 52 interface SimBouncerRepository { 53 /** The subscription id of the current locked sim card. */ 54 val subscriptionId: StateFlow<Int> 55 /** The active subscription of the current subscription id. */ 56 val activeSubscriptionInfo: StateFlow<SubscriptionInfo?> 57 /** 58 * Determines if current sim card is an esim and is locked. 59 * 60 * A null value indicates that we do not know if we are esim locked or not. 61 */ 62 val isLockedEsim: StateFlow<Boolean?> 63 /** 64 * Determines whether the current sim is locked requiring a PUK (Personal Unlocking Key) code. 65 */ 66 val isSimPukLocked: StateFlow<Boolean> 67 /** 68 * The error message that should be displayed in an alert dialog. 69 * 70 * A null value indicates that the error dialog is not showing. 71 */ 72 val errorDialogMessage: StateFlow<String?> 73 /** The state of the user flow on the SimPuk screen. */ 74 val simPukInputModel: SimPukInputModel 75 /** Sets the state of the user flow on the SimPuk screen. */ 76 fun setSimPukUserInput(enteredSimPuk: String? = null, enteredSimPin: String? = null) 77 /** 78 * Sets the error message when failing sim verification. 79 * 80 * A null value indicates that there is no error message to show. 81 */ 82 fun setSimVerificationErrorMessage(msg: String?) 83 } 84 85 @SysUISingleton 86 class SimBouncerRepositoryImpl 87 @Inject 88 constructor( 89 @Application private val applicationScope: CoroutineScope, 90 @Background private val backgroundDispatcher: CoroutineDispatcher, 91 @Main resources: Resources, 92 keyguardUpdateMonitor: KeyguardUpdateMonitor, 93 private val subscriptionManager: SubscriptionManagerProxy, 94 broadcastDispatcher: BroadcastDispatcher, 95 euiccManager: EuiccManager?, 96 ) : SimBouncerRepository { 97 private val isPukScreenAvailable: Boolean = 98 resources.getBoolean(com.android.internal.R.bool.config_enable_puk_unlock_screen) 99 100 private val simBouncerModel: Flow<SimBouncerModel?> = <lambda>null101 conflatedCallbackFlow { 102 val callback = 103 object : KeyguardUpdateMonitorCallback() { 104 override fun onSimStateChanged(subId: Int, slotId: Int, simState: Int) { 105 trySend(Unit) 106 } 107 } 108 keyguardUpdateMonitor.registerCallback(callback) 109 awaitClose { keyguardUpdateMonitor.removeCallback(callback) } 110 } <lambda>null111 .map { 112 // Check to see if there is a locked sim puk card. 113 val pukLockedSubId = 114 withContext(backgroundDispatcher) { 115 keyguardUpdateMonitor.getNextSubIdForState( 116 TelephonyManager.SIM_STATE_PUK_REQUIRED 117 ) 118 } 119 if ( 120 isPukScreenAvailable && 121 subscriptionManager.isValidSubscriptionId(pukLockedSubId) 122 ) { 123 return@map (SimBouncerModel(isSimPukLocked = true, pukLockedSubId)) 124 } 125 126 // If there is no locked sim puk card, check to see if there is a locked sim card. 127 val pinLockedSubId = 128 withContext(backgroundDispatcher) { 129 keyguardUpdateMonitor.getNextSubIdForState( 130 TelephonyManager.SIM_STATE_PIN_REQUIRED 131 ) 132 } 133 if (subscriptionManager.isValidSubscriptionId(pinLockedSubId)) { 134 return@map SimBouncerModel(isSimPukLocked = false, pinLockedSubId) 135 } 136 137 return@map null // There is no locked sim. 138 } 139 140 override val subscriptionId: StateFlow<Int> = 141 simBouncerModel statenull142 .map { state -> state?.subscriptionId ?: INVALID_SUBSCRIPTION_ID } 143 .stateIn( 144 scope = applicationScope, 145 started = SharingStarted.WhileSubscribed(), 146 initialValue = INVALID_SUBSCRIPTION_ID, 147 ) 148 149 @SuppressLint("MissingPermission") 150 override val activeSubscriptionInfo: StateFlow<SubscriptionInfo?> = 151 subscriptionId <lambda>null152 .map { 153 withContext(backgroundDispatcher) { 154 subscriptionManager.getActiveSubscriptionInfo(it) 155 } 156 } 157 .stateIn( 158 scope = applicationScope, 159 started = SharingStarted.Eagerly, 160 initialValue = null, 161 ) 162 163 @SuppressLint("MissingPermission") 164 override val isLockedEsim: StateFlow<Boolean?> = 165 activeSubscriptionInfo infonull166 .map { info -> 167 info?.let { euiccManager != null && euiccManager.isEnabled && info.isEmbedded } 168 } 169 .stateIn( 170 scope = applicationScope, 171 started = SharingStarted.Eagerly, 172 initialValue = null, 173 ) 174 175 override val isSimPukLocked: StateFlow<Boolean> = 176 simBouncerModel <lambda>null177 .map { it?.isSimPukLocked == true } 178 .stateIn( 179 scope = applicationScope, 180 started = SharingStarted.Eagerly, 181 initialValue = false, 182 ) 183 184 private val disableEsimErrorMessage: Flow<String?> = receivernull185 broadcastDispatcher.broadcastFlow(filter = IntentFilter(ACTION_DISABLE_ESIM)) { _, receiver 186 -> 187 if (receiver.resultCode != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) { 188 resources.getString(R.string.error_disable_esim_msg) 189 } else { 190 null 191 } 192 } 193 194 private val simVerificationErrorMessage: MutableStateFlow<String?> = MutableStateFlow(null) 195 196 override val errorDialogMessage: StateFlow<String?> = 197 merge(disableEsimErrorMessage, simVerificationErrorMessage) 198 .stateIn( 199 scope = applicationScope, 200 started = SharingStarted.WhileSubscribed(), 201 initialValue = null, 202 ) 203 204 private var _simPukInputModel: SimPukInputModel = SimPukInputModel() 205 override val simPukInputModel: SimPukInputModel 206 get() = _simPukInputModel 207 setSimPukUserInputnull208 override fun setSimPukUserInput(enteredSimPuk: String?, enteredSimPin: String?) { 209 _simPukInputModel = SimPukInputModel(enteredSimPuk, enteredSimPin) 210 } 211 setSimVerificationErrorMessagenull212 override fun setSimVerificationErrorMessage(msg: String?) { 213 simVerificationErrorMessage.value = msg 214 } 215 216 companion object { 217 const val ACTION_DISABLE_ESIM = "com.android.keyguard.disable_esim" 218 const val INVALID_SUBSCRIPTION_ID = SubscriptionManager.INVALID_SUBSCRIPTION_ID 219 } 220 } 221