Merge "Add TrustRepository to provide a source of truth for trust grant changes" into tm-qpr-dev am: f012899574
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20789657 Change-Id: Ia486fbde0e761669d97d68647c6cd083d0b3b0c2 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.keyguard.logging
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.keyguard.shared.model.TrustModel
|
||||
import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
|
||||
import com.android.systemui.plugins.log.LogBuffer
|
||||
import com.android.systemui.plugins.log.LogLevel
|
||||
import javax.inject.Inject
|
||||
|
||||
/** Logging helper for trust repository. */
|
||||
@SysUISingleton
|
||||
class TrustRepositoryLogger
|
||||
@Inject
|
||||
constructor(
|
||||
@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer,
|
||||
) {
|
||||
fun onTrustChanged(
|
||||
enabled: Boolean,
|
||||
newlyUnlocked: Boolean,
|
||||
userId: Int,
|
||||
flags: Int,
|
||||
trustGrantedMessages: List<String>?
|
||||
) {
|
||||
logBuffer.log(
|
||||
TAG,
|
||||
LogLevel.DEBUG,
|
||||
{
|
||||
bool1 = enabled
|
||||
bool2 = newlyUnlocked
|
||||
int1 = userId
|
||||
int2 = flags
|
||||
str1 = trustGrantedMessages?.joinToString()
|
||||
},
|
||||
{
|
||||
"onTrustChanged enabled: $bool1, newlyUnlocked: $bool2, " +
|
||||
"userId: $int1, flags: $int2, grantMessages: $str1"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun trustListenerRegistered() {
|
||||
logBuffer.log(TAG, LogLevel.VERBOSE, "TrustRepository#registerTrustListener")
|
||||
}
|
||||
|
||||
fun trustListenerUnregistered() {
|
||||
logBuffer.log(TAG, LogLevel.VERBOSE, "TrustRepository#unregisterTrustListener")
|
||||
}
|
||||
|
||||
fun trustModelEmitted(value: TrustModel) {
|
||||
logBuffer.log(
|
||||
TAG,
|
||||
LogLevel.DEBUG,
|
||||
{
|
||||
int1 = value.userId
|
||||
bool1 = value.isTrusted
|
||||
},
|
||||
{ "trustModel emitted: userId: $int1 isTrusted: $bool1" }
|
||||
)
|
||||
}
|
||||
|
||||
fun isCurrentUserTrusted(isCurrentUserTrusted: Boolean) {
|
||||
logBuffer.log(
|
||||
TAG,
|
||||
LogLevel.DEBUG,
|
||||
{ bool1 = isCurrentUserTrusted },
|
||||
{ "isCurrentUserTrusted emitted: $bool1" }
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "TrustRepositoryLog"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.keyguard.data.repository
|
||||
|
||||
import android.app.trust.TrustManager
|
||||
import com.android.keyguard.logging.TrustRepositoryLogger
|
||||
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
|
||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.keyguard.shared.model.TrustModel
|
||||
import com.android.systemui.user.data.repository.UserRepository
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
|
||||
/** Encapsulates any state relevant to trust agents and trust grants. */
|
||||
interface TrustRepository {
|
||||
/** Flow representing whether the current user is trusted. */
|
||||
val isCurrentUserTrusted: Flow<Boolean>
|
||||
}
|
||||
|
||||
@SysUISingleton
|
||||
class TrustRepositoryImpl
|
||||
@Inject
|
||||
constructor(
|
||||
@Application private val applicationScope: CoroutineScope,
|
||||
private val userRepository: UserRepository,
|
||||
private val trustManager: TrustManager,
|
||||
private val logger: TrustRepositoryLogger,
|
||||
) : TrustRepository {
|
||||
private val latestTrustModelForUser = mutableMapOf<Int, TrustModel>()
|
||||
|
||||
private val trust =
|
||||
conflatedCallbackFlow {
|
||||
val callback =
|
||||
object : TrustManager.TrustListener {
|
||||
override fun onTrustChanged(
|
||||
enabled: Boolean,
|
||||
newlyUnlocked: Boolean,
|
||||
userId: Int,
|
||||
flags: Int,
|
||||
grantMsgs: List<String>?
|
||||
) {
|
||||
logger.onTrustChanged(enabled, newlyUnlocked, userId, flags, grantMsgs)
|
||||
trySendWithFailureLogging(
|
||||
TrustModel(enabled, userId),
|
||||
TrustRepositoryLogger.TAG,
|
||||
"onTrustChanged"
|
||||
)
|
||||
}
|
||||
|
||||
override fun onTrustError(message: CharSequence?) = Unit
|
||||
|
||||
override fun onTrustManagedChanged(enabled: Boolean, userId: Int) = Unit
|
||||
}
|
||||
trustManager.registerTrustListener(callback)
|
||||
logger.trustListenerRegistered()
|
||||
awaitClose {
|
||||
logger.trustListenerUnregistered()
|
||||
trustManager.unregisterTrustListener(callback)
|
||||
}
|
||||
}
|
||||
.onEach {
|
||||
latestTrustModelForUser[it.userId] = it
|
||||
logger.trustModelEmitted(it)
|
||||
}
|
||||
.shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1)
|
||||
|
||||
override val isCurrentUserTrusted: Flow<Boolean>
|
||||
get() =
|
||||
combine(trust, userRepository.selectedUserInfo, ::Pair)
|
||||
.map { latestTrustModelForUser[it.second.id]?.isTrusted ?: false }
|
||||
.distinctUntilChanged()
|
||||
.onEach { logger.isCurrentUserTrusted(it) }
|
||||
.onStart { emit(false) }
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.keyguard.shared.model
|
||||
|
||||
/** Represents the trust state */
|
||||
data class TrustModel(
|
||||
/** If true, the system believes the environment to be trusted. */
|
||||
val isTrusted: Boolean,
|
||||
/** The user, for which the trust changed. */
|
||||
val userId: Int,
|
||||
)
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.keyguard.data.repository
|
||||
|
||||
import android.app.trust.TrustManager
|
||||
import android.content.pm.UserInfo
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.keyguard.logging.TrustRepositoryLogger
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.coroutines.collectLastValue
|
||||
import com.android.systemui.plugins.log.LogBuffer
|
||||
import com.android.systemui.plugins.log.LogcatEchoTracker
|
||||
import com.android.systemui.user.data.repository.FakeUserRepository
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class TrustRepositoryTest : SysuiTestCase() {
|
||||
@Mock private lateinit var trustManager: TrustManager
|
||||
@Captor private lateinit var listenerCaptor: ArgumentCaptor<TrustManager.TrustListener>
|
||||
private lateinit var userRepository: FakeUserRepository
|
||||
private lateinit var testScope: TestScope
|
||||
private val users = listOf(UserInfo(1, "user 1", 0), UserInfo(2, "user 1", 0))
|
||||
|
||||
private lateinit var underTest: TrustRepository
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
testScope = TestScope()
|
||||
userRepository = FakeUserRepository()
|
||||
userRepository.setUserInfos(users)
|
||||
|
||||
val logger =
|
||||
TrustRepositoryLogger(
|
||||
LogBuffer("TestBuffer", 1, mock(LogcatEchoTracker::class.java), false)
|
||||
)
|
||||
underTest =
|
||||
TrustRepositoryImpl(testScope.backgroundScope, userRepository, trustManager, logger)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isCurrentUserTrusted_whenTrustChanges_emitsLatestValue() =
|
||||
testScope.runTest {
|
||||
runCurrent()
|
||||
verify(trustManager).registerTrustListener(listenerCaptor.capture())
|
||||
val listener = listenerCaptor.value
|
||||
|
||||
val currentUserId = users[0].id
|
||||
userRepository.setSelectedUserInfo(users[0])
|
||||
val isCurrentUserTrusted = collectLastValue(underTest.isCurrentUserTrusted)
|
||||
|
||||
listener.onTrustChanged(true, false, currentUserId, 0, emptyList())
|
||||
assertThat(isCurrentUserTrusted()).isTrue()
|
||||
|
||||
listener.onTrustChanged(false, false, currentUserId, 0, emptyList())
|
||||
|
||||
assertThat(isCurrentUserTrusted()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isCurrentUserTrusted_isFalse_byDefault() =
|
||||
testScope.runTest {
|
||||
runCurrent()
|
||||
|
||||
val isCurrentUserTrusted = collectLastValue(underTest.isCurrentUserTrusted)
|
||||
|
||||
assertThat(isCurrentUserTrusted()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isCurrentUserTrusted_whenTrustChangesForDifferentUser_noop() =
|
||||
testScope.runTest {
|
||||
runCurrent()
|
||||
verify(trustManager).registerTrustListener(listenerCaptor.capture())
|
||||
userRepository.setSelectedUserInfo(users[0])
|
||||
val listener = listenerCaptor.value
|
||||
|
||||
val isCurrentUserTrusted = collectLastValue(underTest.isCurrentUserTrusted)
|
||||
// current user is trusted.
|
||||
listener.onTrustChanged(true, true, users[0].id, 0, emptyList())
|
||||
// some other user is not trusted.
|
||||
listener.onTrustChanged(false, false, users[1].id, 0, emptyList())
|
||||
|
||||
assertThat(isCurrentUserTrusted()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isCurrentUserTrusted_whenTrustChangesForCurrentUser_emitsNewValue() =
|
||||
testScope.runTest {
|
||||
runCurrent()
|
||||
verify(trustManager).registerTrustListener(listenerCaptor.capture())
|
||||
val listener = listenerCaptor.value
|
||||
userRepository.setSelectedUserInfo(users[0])
|
||||
|
||||
val isCurrentUserTrusted = collectLastValue(underTest.isCurrentUserTrusted)
|
||||
listener.onTrustChanged(true, true, users[0].id, 0, emptyList())
|
||||
assertThat(isCurrentUserTrusted()).isTrue()
|
||||
|
||||
listener.onTrustChanged(false, true, users[0].id, 0, emptyList())
|
||||
assertThat(isCurrentUserTrusted()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isCurrentUserTrusted_whenUserChangesWithoutRecentTrustChange_defaultsToFalse() =
|
||||
testScope.runTest {
|
||||
runCurrent()
|
||||
verify(trustManager).registerTrustListener(listenerCaptor.capture())
|
||||
val listener = listenerCaptor.value
|
||||
userRepository.setSelectedUserInfo(users[0])
|
||||
listener.onTrustChanged(true, true, users[0].id, 0, emptyList())
|
||||
|
||||
val isCurrentUserTrusted = collectLastValue(underTest.isCurrentUserTrusted)
|
||||
userRepository.setSelectedUserInfo(users[1])
|
||||
|
||||
assertThat(isCurrentUserTrusted()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isCurrentUserTrusted_trustChangesFirstBeforeUserInfoChanges_emitsCorrectValue() =
|
||||
testScope.runTest {
|
||||
runCurrent()
|
||||
verify(trustManager).registerTrustListener(listenerCaptor.capture())
|
||||
val listener = listenerCaptor.value
|
||||
val isCurrentUserTrusted = collectLastValue(underTest.isCurrentUserTrusted)
|
||||
|
||||
listener.onTrustChanged(true, true, users[0].id, 0, emptyList())
|
||||
assertThat(isCurrentUserTrusted()).isFalse()
|
||||
|
||||
userRepository.setSelectedUserInfo(users[0])
|
||||
|
||||
assertThat(isCurrentUserTrusted()).isTrue()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user