1 /*
2  * Copyright 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 @file:OptIn(ExperimentalCoroutinesApi::class)
18 
19 package com.android.systemui.authentication.data.repository
20 
21 import android.app.admin.DevicePolicyManager
22 import android.content.Intent
23 import android.content.pm.UserInfo
24 import androidx.test.filters.SmallTest
25 import com.android.internal.widget.LockPatternUtils
26 import com.android.keyguard.KeyguardSecurityModel
27 import com.android.systemui.SysuiTestCase
28 import com.android.systemui.authentication.data.model.AuthenticationMethodModel
29 import com.android.systemui.coroutines.collectLastValue
30 import com.android.systemui.coroutines.collectValues
31 import com.android.systemui.scene.SceneTestUtils
32 import com.android.systemui.user.data.repository.FakeUserRepository
33 import com.android.systemui.util.mockito.whenever
34 import com.google.common.truth.Truth.assertThat
35 import java.util.function.Function
36 import kotlinx.coroutines.ExperimentalCoroutinesApi
37 import kotlinx.coroutines.runBlocking
38 import kotlinx.coroutines.test.runCurrent
39 import kotlinx.coroutines.test.runTest
40 import org.junit.Before
41 import org.junit.Test
42 import org.junit.runner.RunWith
43 import org.junit.runners.JUnit4
44 import org.mockito.ArgumentMatchers.anyInt
45 import org.mockito.Mock
46 import org.mockito.MockitoAnnotations
47 
48 @SmallTest
49 @RunWith(JUnit4::class)
50 class AuthenticationRepositoryTest : SysuiTestCase() {
51 
52     @Mock private lateinit var lockPatternUtils: LockPatternUtils
53     @Mock private lateinit var getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>
54 
55     private val testUtils = SceneTestUtils(this)
56     private val testScope = testUtils.testScope
57     private val userRepository = FakeUserRepository()
58 
59     private lateinit var underTest: AuthenticationRepository
60 
61     private var currentSecurityMode: KeyguardSecurityModel.SecurityMode =
62         KeyguardSecurityModel.SecurityMode.PIN
63 
64     @Before
65     fun setUp() {
66         MockitoAnnotations.initMocks(this)
67         userRepository.setUserInfos(USER_INFOS)
68         runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) }
69         whenever(getSecurityMode.apply(anyInt())).thenAnswer { currentSecurityMode }
70 
71         underTest =
72             AuthenticationRepositoryImpl(
73                 applicationScope = testScope.backgroundScope,
74                 getSecurityMode = getSecurityMode,
75                 backgroundDispatcher = testUtils.testDispatcher,
76                 userRepository = userRepository,
77                 keyguardRepository = testUtils.keyguardRepository,
78                 lockPatternUtils = lockPatternUtils,
79                 broadcastDispatcher = fakeBroadcastDispatcher,
80             )
81     }
82 
83     @Test
84     fun authenticationMethod() =
85         testScope.runTest {
86             val authMethod by collectLastValue(underTest.authenticationMethod)
87             runCurrent()
88             dispatchBroadcast()
89             assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pin)
90             assertThat(underTest.getAuthenticationMethod()).isEqualTo(AuthenticationMethodModel.Pin)
91 
92             setSecurityModeAndDispatchBroadcast(KeyguardSecurityModel.SecurityMode.Pattern)
93             assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pattern)
94             assertThat(underTest.getAuthenticationMethod())
95                 .isEqualTo(AuthenticationMethodModel.Pattern)
96 
97             setSecurityModeAndDispatchBroadcast(KeyguardSecurityModel.SecurityMode.None)
98             assertThat(authMethod).isEqualTo(AuthenticationMethodModel.None)
99             assertThat(underTest.getAuthenticationMethod())
100                 .isEqualTo(AuthenticationMethodModel.None)
101         }
102 
103     @Test
104     fun isAutoConfirmEnabled() =
105         testScope.runTest {
106             whenever(lockPatternUtils.isAutoPinConfirmEnabled(USER_INFOS[0].id)).thenReturn(true)
107             whenever(lockPatternUtils.isAutoPinConfirmEnabled(USER_INFOS[1].id)).thenReturn(false)
108 
109             val values by collectValues(underTest.isAutoConfirmEnabled)
110             assertThat(values.first()).isFalse()
111             assertThat(values.last()).isTrue()
112 
113             userRepository.setSelectedUserInfo(USER_INFOS[1])
114             assertThat(values.last()).isFalse()
115         }
116 
117     @Test
118     fun isPatternVisible() =
119         testScope.runTest {
120             whenever(lockPatternUtils.isVisiblePatternEnabled(USER_INFOS[0].id)).thenReturn(false)
121             whenever(lockPatternUtils.isVisiblePatternEnabled(USER_INFOS[1].id)).thenReturn(true)
122 
123             val values by collectValues(underTest.isPatternVisible)
124             assertThat(values.first()).isTrue()
125             assertThat(values.last()).isFalse()
126 
127             userRepository.setSelectedUserInfo(USER_INFOS[1])
128             assertThat(values.last()).isTrue()
129         }
130 
131     private fun setSecurityModeAndDispatchBroadcast(
132         securityMode: KeyguardSecurityModel.SecurityMode,
133     ) {
134         currentSecurityMode = securityMode
135         dispatchBroadcast()
136     }
137 
138     private fun dispatchBroadcast() {
139         fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
140             context,
141             Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)
142         )
143     }
144 
145     companion object {
146         private val USER_INFOS =
147             listOf(
148                 UserInfo(
149                     /* id= */ 100,
150                     /* name= */ "First user",
151                     /* flags= */ 0,
152                 ),
153                 UserInfo(
154                     /* id= */ 101,
155                     /* name= */ "Second user",
156                     /* flags= */ 0,
157                 ),
158             )
159     }
160 }
161