• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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