1 /* <lambda>null2 * Copyright (C) 2024 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.scene.domain.startable 18 19 import android.os.DeadObjectException 20 import android.os.RemoteException 21 import com.android.internal.policy.IKeyguardStateCallback 22 import com.android.systemui.CoreStartable 23 import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor 24 import com.android.systemui.dagger.SysUISingleton 25 import com.android.systemui.dagger.qualifiers.Application 26 import com.android.systemui.dagger.qualifiers.Background 27 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor 28 import com.android.systemui.keyguard.domain.interactor.TrustInteractor 29 import com.android.systemui.scene.domain.interactor.SceneInteractor 30 import com.android.systemui.scene.shared.flag.SceneContainerFlag 31 import com.android.systemui.scene.shared.model.Scenes 32 import com.android.systemui.user.domain.interactor.SelectedUserInteractor 33 import javax.inject.Inject 34 import kotlinx.coroutines.CoroutineDispatcher 35 import kotlinx.coroutines.CoroutineScope 36 import kotlinx.coroutines.flow.collectLatest 37 import kotlinx.coroutines.flow.combine 38 import kotlinx.coroutines.flow.distinctUntilChanged 39 import kotlinx.coroutines.flow.filter 40 import kotlinx.coroutines.flow.map 41 import com.android.app.tracing.coroutines.launchTraced as launch 42 import kotlinx.coroutines.withContext 43 44 /** Keeps all [IKeyguardStateCallback]s hydrated with the latest state. */ 45 @SysUISingleton 46 class KeyguardStateCallbackStartable 47 @Inject 48 constructor( 49 @Application private val applicationScope: CoroutineScope, 50 @Background private val backgroundDispatcher: CoroutineDispatcher, 51 private val sceneInteractor: SceneInteractor, 52 private val selectedUserInteractor: SelectedUserInteractor, 53 private val deviceEntryInteractor: DeviceEntryInteractor, 54 private val simBouncerInteractor: SimBouncerInteractor, 55 private val trustInteractor: TrustInteractor, 56 ) : CoreStartable { 57 58 private val callbacks = mutableListOf<IKeyguardStateCallback>() 59 60 override fun start() { 61 if (!SceneContainerFlag.isEnabled) { 62 return 63 } 64 65 hydrateKeyguardShowingAndInputRestrictionStates() 66 hydrateSimSecureState() 67 notifyWhenKeyguardShowingChanged() 68 notifyWhenTrustChanged() 69 } 70 71 fun addCallback(callback: IKeyguardStateCallback) { 72 SceneContainerFlag.unsafeAssertInNewMode() 73 74 callbacks.add(callback) 75 76 applicationScope.launch(context = backgroundDispatcher) { 77 callback.onShowingStateChanged( 78 !deviceEntryInteractor.isDeviceEntered.value, 79 selectedUserInteractor.getSelectedUserId(), 80 ) 81 callback.onTrustedChanged(trustInteractor.isTrusted.value) 82 callback.onSimSecureStateChanged(simBouncerInteractor.isAnySimSecure.value) 83 // TODO(b/348644111): add support for mNeedToReshowWhenReenabled 84 callback.onInputRestrictedStateChanged(!deviceEntryInteractor.isDeviceEntered.value) 85 } 86 } 87 88 private fun hydrateKeyguardShowingAndInputRestrictionStates() { 89 applicationScope.launch { 90 combine( 91 selectedUserInteractor.selectedUser, 92 deviceEntryInteractor.isDeviceEntered, 93 ::Pair 94 ) 95 .collectLatest { (selectedUserId, isDeviceEntered) -> 96 val iterator = callbacks.iterator() 97 withContext(backgroundDispatcher) { 98 while (iterator.hasNext()) { 99 val callback = iterator.next() 100 try { 101 callback.onShowingStateChanged(!isDeviceEntered, selectedUserId) 102 // TODO(b/348644111): add support for mNeedToReshowWhenReenabled 103 callback.onInputRestrictedStateChanged(!isDeviceEntered) 104 } catch (e: RemoteException) { 105 if (e is DeadObjectException) { 106 iterator.remove() 107 } 108 } 109 } 110 } 111 } 112 } 113 } 114 115 private fun hydrateSimSecureState() { 116 applicationScope.launch { 117 simBouncerInteractor.isAnySimSecure.collectLatest { isSimSecured -> 118 val iterator = callbacks.iterator() 119 withContext(backgroundDispatcher) { 120 while (iterator.hasNext()) { 121 val callback = iterator.next() 122 try { 123 callback.onSimSecureStateChanged(isSimSecured) 124 } catch (e: RemoteException) { 125 if (e is DeadObjectException) { 126 iterator.remove() 127 } 128 } 129 } 130 } 131 } 132 } 133 } 134 135 private fun notifyWhenKeyguardShowingChanged() { 136 applicationScope.launch { 137 // This is equivalent to isDeviceEntered but it waits for the full transition animation 138 // to finish before emitting a new value and not just for the current scene to be 139 // switched. 140 sceneInteractor.transitionState 141 .filter { it.isIdle(Scenes.Gone) || it.isIdle(Scenes.Lockscreen) } 142 .map { it.isIdle(Scenes.Lockscreen) } 143 .distinctUntilChanged() 144 .collectLatest { trustInteractor.reportKeyguardShowingChanged() } 145 } 146 } 147 148 private fun notifyWhenTrustChanged() { 149 applicationScope.launch { 150 trustInteractor.isTrusted.collectLatest { isTrusted -> 151 val iterator = callbacks.iterator() 152 withContext(backgroundDispatcher) { 153 while (iterator.hasNext()) { 154 val callback = iterator.next() 155 try { 156 callback.onTrustedChanged(isTrusted) 157 } catch (e: RemoteException) { 158 if (e is DeadObjectException) { 159 iterator.remove() 160 } 161 } 162 } 163 } 164 } 165 } 166 } 167 } 168