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.bouncer.data.factory
18 
19 import androidx.test.ext.junit.runners.AndroidJUnit4
20 import androidx.test.filters.SmallTest
21 import com.android.keyguard.KeyguardSecurityModel
22 import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
23 import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password
24 import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Pattern
25 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
26 import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
27 import com.android.systemui.SysuiTestCase
28 import com.android.systemui.bouncer.shared.model.BouncerMessageModel
29 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
30 import com.android.systemui.util.mockito.whenever
31 import com.google.common.truth.StringSubject
32 import com.google.common.truth.Truth.assertThat
33 import kotlinx.coroutines.test.TestScope
34 import kotlinx.coroutines.test.runTest
35 import org.junit.Before
36 import org.junit.Test
37 import org.junit.runner.RunWith
38 import org.mockito.Mock
39 import org.mockito.MockitoAnnotations
40 
41 @SmallTest
42 @RunWith(AndroidJUnit4::class)
43 class BouncerMessageFactoryTest : SysuiTestCase() {
44     private lateinit var underTest: BouncerMessageFactory
45 
46     @Mock private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
47 
48     @Mock private lateinit var securityModel: KeyguardSecurityModel
49 
50     private lateinit var testScope: TestScope
51 
52     @Before
53     fun setUp() {
54         MockitoAnnotations.initMocks(this)
55         testScope = TestScope()
56         biometricSettingsRepository = FakeBiometricSettingsRepository()
57         underTest = BouncerMessageFactory(biometricSettingsRepository, securityModel)
58     }
59 
60     @Test
61     fun bouncerMessages_choosesTheRightMessage_basedOnSecurityModeAndFpAuthIsAllowed() =
62         testScope.runTest {
63             primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAuthAllowed = false)
64                 .isEqualTo("Enter PIN")
65             primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAuthAllowed = true)
66                 .isEqualTo("Unlock with PIN or fingerprint")
67 
68             primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAuthAllowed = false)
69                 .isEqualTo("Enter password")
70             primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAuthAllowed = true)
71                 .isEqualTo("Unlock with password or fingerprint")
72 
73             primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAuthAllowed = false)
74                 .isEqualTo("Draw pattern")
75             primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAuthAllowed = true)
76                 .isEqualTo("Unlock with pattern or fingerprint")
77         }
78 
79     @Test
80     fun bouncerMessages_overridesSecondaryMessageValue() =
81         testScope.runTest {
82             val bouncerMessageModel =
83                 bouncerMessageModel(
84                     PIN,
85                     true,
86                     PROMPT_REASON_DEFAULT,
87                     secondaryMessageOverride = "face acquisition message"
88                 )!!
89             assertThat(context.resources.getString(bouncerMessageModel.message!!.messageResId!!))
90                 .isEqualTo("Unlock with PIN or fingerprint")
91             assertThat(bouncerMessageModel.secondaryMessage!!.message!!)
92                 .isEqualTo("face acquisition message")
93         }
94 
95     @Test
96     fun bouncerMessages_setsPrimaryAndSecondaryMessage_basedOnSecurityModeAndFpAuthIsAllowed() =
97         testScope.runTest {
98             primaryMessage(
99                     PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
100                     mode = PIN,
101                     fpAuthAllowed = true
102                 )
103                 .isEqualTo("Wrong PIN. Try again.")
104             secondaryMessage(
105                     PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
106                     mode = PIN,
107                     fpAuthAllowed = true
108                 )
109                 .isEqualTo("Or unlock with fingerprint")
110 
111             primaryMessage(
112                     PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
113                     mode = Password,
114                     fpAuthAllowed = true
115                 )
116                 .isEqualTo("Wrong password. Try again.")
117             secondaryMessage(
118                     PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
119                     mode = Password,
120                     fpAuthAllowed = true
121                 )
122                 .isEqualTo("Or unlock with fingerprint")
123 
124             primaryMessage(
125                     PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
126                     mode = Pattern,
127                     fpAuthAllowed = true
128                 )
129                 .isEqualTo("Wrong pattern. Try again.")
130             secondaryMessage(
131                     PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
132                     mode = Pattern,
133                     fpAuthAllowed = true
134                 )
135                 .isEqualTo("Or unlock with fingerprint")
136         }
137 
138     private fun primaryMessage(
139         reason: Int,
140         mode: KeyguardSecurityModel.SecurityMode,
141         fpAuthAllowed: Boolean
142     ): StringSubject {
143         return assertThat(
144             context.resources.getString(
145                 bouncerMessageModel(mode, fpAuthAllowed, reason)!!.message!!.messageResId!!
146             )
147         )!!
148     }
149 
150     private fun secondaryMessage(
151         reason: Int,
152         mode: KeyguardSecurityModel.SecurityMode,
153         fpAuthAllowed: Boolean
154     ): StringSubject {
155         return assertThat(
156             context.resources.getString(
157                 bouncerMessageModel(mode, fpAuthAllowed, reason)!!.secondaryMessage!!.messageResId!!
158             )
159         )!!
160     }
161 
162     private fun bouncerMessageModel(
163         mode: KeyguardSecurityModel.SecurityMode,
164         fpAuthAllowed: Boolean,
165         reason: Int,
166         secondaryMessageOverride: String? = null,
167     ): BouncerMessageModel? {
168         whenever(securityModel.getSecurityMode(0)).thenReturn(mode)
169         biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(fpAuthAllowed)
170 
171         return underTest.createFromPromptReason(
172             reason,
173             0,
174             secondaryMsgOverride = secondaryMessageOverride
175         )
176     }
177 }
178