1 /*
2  * 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.keyguard.data.repository
18 
19 import android.app.trust.TrustManager
20 import com.android.keyguard.logging.TrustRepositoryLogger
21 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
22 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
23 import com.android.systemui.dagger.SysUISingleton
24 import com.android.systemui.dagger.qualifiers.Application
25 import com.android.systemui.keyguard.shared.model.ActiveUnlockModel
26 import com.android.systemui.keyguard.shared.model.TrustManagedModel
27 import com.android.systemui.keyguard.shared.model.TrustModel
28 import com.android.systemui.user.data.repository.UserRepository
29 import javax.inject.Inject
30 import kotlinx.coroutines.CoroutineScope
31 import kotlinx.coroutines.channels.awaitClose
32 import kotlinx.coroutines.flow.Flow
33 import kotlinx.coroutines.flow.SharingStarted
34 import kotlinx.coroutines.flow.StateFlow
35 import kotlinx.coroutines.flow.combine
36 import kotlinx.coroutines.flow.distinctUntilChanged
37 import kotlinx.coroutines.flow.map
38 import kotlinx.coroutines.flow.onEach
39 import kotlinx.coroutines.flow.onStart
40 import kotlinx.coroutines.flow.shareIn
41 import kotlinx.coroutines.flow.stateIn
42 
43 /** Encapsulates any state relevant to trust agents and trust grants. */
44 interface TrustRepository {
45     /** Flow representing whether the current user is trusted. */
46     val isCurrentUserTrusted: Flow<Boolean>
47 
48     /** Flow representing whether active unlock is running for the current user. */
49     val isCurrentUserActiveUnlockRunning: Flow<Boolean>
50 
51     /** Reports that whether trust is managed has changed for the current user. */
52     val isCurrentUserTrustManaged: StateFlow<Boolean>
53 }
54 
55 @SysUISingleton
56 class TrustRepositoryImpl
57 @Inject
58 constructor(
59     @Application private val applicationScope: CoroutineScope,
60     private val userRepository: UserRepository,
61     private val trustManager: TrustManager,
62     private val logger: TrustRepositoryLogger,
63 ) : TrustRepository {
64     private val latestTrustModelForUser = mutableMapOf<Int, TrustModel>()
65     private val activeUnlockRunningForUser = mutableMapOf<Int, ActiveUnlockModel>()
66     private val trustManagedForUser = mutableMapOf<Int, TrustManagedModel>()
67 
68     private val trust =
69         conflatedCallbackFlow {
70                 val callback =
71                     object : TrustManager.TrustListener {
72                         override fun onTrustChanged(
73                             enabled: Boolean,
74                             newlyUnlocked: Boolean,
75                             userId: Int,
76                             flags: Int,
77                             grantMsgs: List<String>?
78                         ) {
79                             logger.onTrustChanged(enabled, newlyUnlocked, userId, flags, grantMsgs)
80                             trySendWithFailureLogging(
81                                 TrustModel(enabled, userId),
82                                 TrustRepositoryLogger.TAG,
83                                 "onTrustChanged"
84                             )
85                         }
86 
87                         override fun onTrustError(message: CharSequence?) = Unit
88 
89                         override fun onEnabledTrustAgentsChanged(userId: Int) = Unit
90 
91                         override fun onIsActiveUnlockRunningChanged(
92                             isRunning: Boolean,
93                             userId: Int
94                         ) {
95                             trySendWithFailureLogging(
96                                 ActiveUnlockModel(isRunning, userId),
97                                 TrustRepositoryLogger.TAG,
98                                 "onActiveUnlockRunningChanged"
99                             )
100                         }
101 
102                         override fun onTrustManagedChanged(isTrustManaged: Boolean, userId: Int) {
103                             logger.onTrustManagedChanged(isTrustManaged, userId)
104                             trySendWithFailureLogging(
105                                 TrustManagedModel(userId, isTrustManaged),
106                                 TrustRepositoryLogger.TAG,
107                                 "onTrustManagedChanged"
108                             )
109                         }
110                     }
111                 trustManager.registerTrustListener(callback)
112                 logger.trustListenerRegistered()
113                 awaitClose {
114                     logger.trustListenerUnregistered()
115                     trustManager.unregisterTrustListener(callback)
116                 }
117             }
118             .onEach {
119                 when (it) {
120                     is TrustModel -> {
121                         latestTrustModelForUser[it.userId] = it
122                         logger.trustModelEmitted(it)
123                     }
124                     is ActiveUnlockModel -> {
125                         activeUnlockRunningForUser[it.userId] = it
126                         logger.activeUnlockModelEmitted(it)
127                     }
128                     is TrustManagedModel -> {
129                         trustManagedForUser[it.userId] = it
130                         logger.trustManagedModelEmitted(it)
131                     }
132                 }
133             }
134             .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1)
135 
136     override val isCurrentUserActiveUnlockRunning: Flow<Boolean> =
137         combine(trust, userRepository.selectedUserInfo, ::Pair)
138             .map { activeUnlockRunningForUser[it.second.id]?.isRunning ?: false }
139             .distinctUntilChanged()
140             .onEach { logger.isCurrentUserActiveUnlockRunning(it) }
141             .onStart {
142                 emit(
143                     activeUnlockRunningForUser[userRepository.getSelectedUserInfo().id]?.isRunning
144                         ?: false
145                 )
146             }
147 
148     override val isCurrentUserTrustManaged: StateFlow<Boolean>
149         get() =
150             combine(trust, userRepository.selectedUserInfo, ::Pair)
151                 .map { isUserTrustManaged(it.second.id) }
152                 .distinctUntilChanged()
153                 .onEach { logger.isCurrentUserTrustManaged(it) }
154                 .onStart { emit(false) }
155                 .stateIn(
156                     scope = applicationScope,
157                     started = SharingStarted.WhileSubscribed(),
158                     initialValue = false
159                 )
160 
161     private fun isUserTrustManaged(userId: Int) =
162         trustManagedForUser[userId]?.isTrustManaged ?: false
163 
164     override val isCurrentUserTrusted: Flow<Boolean>
165         get() =
166             combine(trust, userRepository.selectedUserInfo, ::Pair)
167                 .map { latestTrustModelForUser[it.second.id]?.isTrusted ?: false }
168                 .distinctUntilChanged()
169                 .onEach { logger.isCurrentUserTrusted(it) }
170                 .onStart { emit(false) }
171 }
172