1 /* 2 * Copyright (C) 2022 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.keyguard 18 19 import android.content.ContentResolver 20 import android.database.ContentObserver 21 import android.hardware.biometrics.BiometricFaceConstants 22 import android.net.Uri 23 import android.os.Handler 24 import android.os.PowerManager 25 import android.os.PowerManager.WAKE_REASON_BIOMETRIC 26 import android.os.UserHandle 27 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL 28 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO 29 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS 30 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT 31 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED 32 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE 33 import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS 34 import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD 35 import androidx.test.filters.SmallTest 36 import com.android.systemui.SysuiTestCase 37 import com.android.systemui.dump.DumpManager 38 import com.android.systemui.util.mockito.capture 39 import com.android.systemui.util.mockito.eq 40 import com.android.systemui.util.settings.FakeSettings 41 import org.junit.Assert.assertFalse 42 import org.junit.Assert.assertTrue 43 import org.junit.Before 44 import org.junit.Test 45 import org.mockito.ArgumentCaptor 46 import org.mockito.Captor 47 import org.mockito.Mock 48 import org.mockito.Mockito.`when` 49 import org.mockito.Mockito.verify 50 import org.mockito.MockitoAnnotations 51 import java.io.PrintWriter 52 53 @SmallTest 54 class ActiveUnlockConfigTest : SysuiTestCase() { 55 private lateinit var secureSettings: FakeSettings 56 @Mock 57 private lateinit var contentResolver: ContentResolver 58 @Mock 59 private lateinit var handler: Handler 60 @Mock 61 private lateinit var dumpManager: DumpManager 62 @Mock 63 private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor 64 @Mock private lateinit var mockPrintWriter: PrintWriter 65 66 @Captor 67 private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver> 68 69 private lateinit var activeUnlockConfig: ActiveUnlockConfig 70 private var currentUser: Int = 0 71 72 @Before 73 fun setUp() { 74 MockitoAnnotations.initMocks(this) 75 76 currentUser = KeyguardUpdateMonitor.getCurrentUser() 77 secureSettings = FakeSettings() 78 activeUnlockConfig = ActiveUnlockConfig( 79 handler, 80 secureSettings, 81 contentResolver, 82 dumpManager 83 ) 84 } 85 86 @Test 87 fun registersForSettingsChanges() { 88 verifyRegisterSettingObserver() 89 } 90 91 @Test 92 fun onWakeupSettingChanged() { 93 // GIVEN no active unlock settings enabled 94 assertFalse( 95 activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 96 ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE) 97 ) 98 99 // WHEN unlock on wake is allowed 100 secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_WAKE, 1, currentUser) 101 updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)) 102 103 // THEN active unlock triggers allowed on: wake, unlock-intent, and biometric failure 104 assertTrue( 105 activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 106 ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE) 107 ) 108 assertTrue( 109 activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 110 ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT) 111 ) 112 assertTrue( 113 activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 114 ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL) 115 ) 116 } 117 118 @Test 119 fun onUnlockIntentSettingChanged() { 120 // GIVEN no active unlock settings enabled 121 assertFalse( 122 activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 123 ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT) 124 ) 125 126 // WHEN unlock on biometric failed is allowed 127 secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 1, currentUser) 128 updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT)) 129 130 // THEN active unlock triggers allowed on: biometric failure ONLY 131 assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 132 ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE)) 133 assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 134 ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT)) 135 assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 136 ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL)) 137 } 138 139 @Test 140 fun onBioFailSettingChanged() { 141 // GIVEN no active unlock settings enabled and triggering unlock intent on biometric 142 // enrollment setting is disabled (empty string is disabled, null would use the default) 143 secureSettings.putStringForUser( 144 ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, "", currentUser) 145 updateSetting(secureSettings.getUriFor( 146 ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED 147 )) 148 assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 149 ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL)) 150 151 // WHEN unlock on biometric failed is allowed 152 secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser) 153 updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)) 154 155 // THEN active unlock triggers allowed on: biometric failure ONLY 156 assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 157 ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE)) 158 assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 159 ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT)) 160 assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 161 ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL)) 162 } 163 164 @Test 165 fun faceErrorSettingsChanged() { 166 // GIVEN unlock on biometric fail 167 secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser) 168 updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)) 169 170 // WHEN face error timeout (3), allow trigger active unlock 171 secureSettings.putStringForUser( 172 ACTIVE_UNLOCK_ON_FACE_ERRORS, "3", currentUser) 173 updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS)) 174 175 // THEN active unlock triggers allowed on error TIMEOUT 176 assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError( 177 BiometricFaceConstants.FACE_ERROR_TIMEOUT)) 178 179 assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError( 180 BiometricFaceConstants.FACE_ERROR_CANCELED)) 181 } 182 183 @Test 184 fun faceAcquiredSettingsChanged() { 185 // GIVEN unlock on biometric fail 186 secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, "1", currentUser) 187 updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)) 188 189 // WHEN face acquiredMsg DARK_GLASSESand MOUTH_COVERING are allowed to trigger 190 secureSettings.putStringForUser( 191 ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, 192 "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" + 193 "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}", 194 currentUser) 195 updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO)) 196 197 // THEN active unlock triggers allowed on acquired messages DARK_GLASSES & MOUTH_COVERING 198 assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( 199 BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED)) 200 assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( 201 BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED)) 202 203 assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( 204 BiometricFaceConstants.FACE_ACQUIRED_GOOD)) 205 assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( 206 BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED)) 207 } 208 209 @Test 210 fun triggerOnUnlockIntentWhenBiometricEnrolledNone() { 211 // GIVEN unlock on biometric fail 212 secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser) 213 updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)) 214 215 // GIVEN fingerprint and face are NOT enrolled 216 activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor 217 `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false) 218 `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false) 219 220 // WHEN unlock intent is allowed when NO biometrics are enrolled (0) 221 222 secureSettings.putStringForUser( 223 ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, 224 "${ActiveUnlockConfig.BiometricType.NONE.intValue}", currentUser) 225 updateSetting(secureSettings.getUriFor( 226 ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED 227 )) 228 229 // THEN active unlock triggers allowed on unlock intent 230 assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 231 ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT)) 232 } 233 234 @Test 235 fun triggerOnUnlockIntentWhenBiometricEnrolledFingerprintOrFaceOnly() { 236 // GIVEN unlock on biometric fail 237 secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser) 238 updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)) 239 240 // GIVEN fingerprint and face are both enrolled 241 activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor 242 `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true) 243 `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true) 244 245 // WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs 246 // are enrolled 247 secureSettings.putStringForUser( 248 ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, 249 "${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" + 250 "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}", 251 currentUser) 252 updateSetting(secureSettings.getUriFor( 253 ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED 254 )) 255 256 // THEN active unlock triggers NOT allowed on unlock intent 257 assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 258 ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT)) 259 260 // WHEN fingerprint ONLY enrolled 261 `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false) 262 `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true) 263 264 // THEN active unlock triggers allowed on unlock intent 265 assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 266 ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT)) 267 268 // WHEN face ONLY enrolled 269 `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true) 270 `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false) 271 272 // THEN active unlock triggers allowed on unlock intent 273 assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( 274 ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT)) 275 } 276 277 @Test 278 fun isWakeupConsideredUnlockIntent_singleValue() { 279 // GIVEN lift is considered an unlock intent 280 secureSettings.putIntForUser( 281 ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS, 282 PowerManager.WAKE_REASON_LIFT, 283 currentUser) 284 updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS)) 285 286 // THEN only WAKE_REASON_LIFT is considered an unlock intent 287 for (wakeReason in 0..WAKE_REASON_BIOMETRIC) { 288 if (wakeReason == PowerManager.WAKE_REASON_LIFT) { 289 assertTrue(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason)) 290 } else { 291 assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason)) 292 } 293 } 294 } 295 296 @Test 297 fun isWakeupConsideredUnlockIntent_multiValue() { 298 // GIVEN lift and tap are considered an unlock intent 299 secureSettings.putStringForUser( 300 ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS, 301 PowerManager.WAKE_REASON_LIFT.toString() + 302 "|" + 303 PowerManager.WAKE_REASON_TAP.toString(), 304 currentUser 305 ) 306 updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS)) 307 308 // THEN WAKE_REASON_LIFT and WAKE_REASON TAP are considered an unlock intent 309 for (wakeReason in 0..WAKE_REASON_BIOMETRIC) { 310 if (wakeReason == PowerManager.WAKE_REASON_LIFT || 311 wakeReason == PowerManager.WAKE_REASON_TAP) { 312 assertTrue(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason)) 313 } else { 314 assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason)) 315 } 316 } 317 assertTrue(activeUnlockConfig.isWakeupConsideredUnlockIntent(PowerManager.WAKE_REASON_LIFT)) 318 assertTrue(activeUnlockConfig.isWakeupConsideredUnlockIntent(PowerManager.WAKE_REASON_TAP)) 319 assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent( 320 PowerManager.WAKE_REASON_UNFOLD_DEVICE)) 321 } 322 323 @Test 324 fun isWakeupConsideredUnlockIntent_emptyValues() { 325 // GIVEN lift and tap are considered an unlock intent 326 secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS, " ", 327 currentUser) 328 updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS)) 329 330 // THEN no wake up gestures are considered an unlock intent 331 for (wakeReason in 0..WAKE_REASON_BIOMETRIC) { 332 assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason)) 333 } 334 assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent( 335 PowerManager.WAKE_REASON_LIFT)) 336 assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(PowerManager.WAKE_REASON_TAP)) 337 assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent( 338 PowerManager.WAKE_REASON_UNFOLD_DEVICE)) 339 } 340 341 @Test 342 fun isWakeupForceDismissKeyguard_singleValue() { 343 verifyRegisterSettingObserver() 344 345 // GIVEN lift is considered an unlock intent 346 secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD, 347 PowerManager.WAKE_REASON_LIFT.toString(), currentUser) 348 updateSetting(secureSettings.getUriFor( 349 ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD 350 )) 351 352 // THEN only WAKE_REASON_LIFT is considered an unlock intent 353 for (wakeReason in 0..WAKE_REASON_BIOMETRIC) { 354 if (wakeReason == PowerManager.WAKE_REASON_LIFT) { 355 assertTrue(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason)) 356 } else { 357 assertFalse(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason)) 358 } 359 } 360 } 361 362 @Test 363 fun isWakeupForceDismissKeyguard_emptyValues() { 364 verifyRegisterSettingObserver() 365 366 // GIVEN lift and tap are considered an unlock intent 367 secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD, 368 " ", currentUser) 369 updateSetting(secureSettings.getUriFor( 370 ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD 371 )) 372 373 // THEN no wake up gestures are considered an unlock intent 374 for (wakeReason in 0..WAKE_REASON_BIOMETRIC) { 375 assertFalse(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason)) 376 } 377 } 378 379 @Test 380 fun isWakeupForceDismissKeyguard_multiValue() { 381 verifyRegisterSettingObserver() 382 383 // GIVEN lift and tap are considered an unlock intent 384 secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD, 385 PowerManager.WAKE_REASON_LIFT.toString() + 386 "|" + 387 PowerManager.WAKE_REASON_TAP.toString(), 388 currentUser 389 ) 390 updateSetting(secureSettings.getUriFor( 391 ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD 392 )) 393 394 // THEN WAKE_REASON_LIFT and WAKE_REASON TAP are considered an unlock intent 395 for (wakeReason in 0..WAKE_REASON_BIOMETRIC) { 396 if (wakeReason == PowerManager.WAKE_REASON_LIFT || 397 wakeReason == PowerManager.WAKE_REASON_TAP) { 398 assertTrue(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason)) 399 } else { 400 assertFalse(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason)) 401 } 402 } 403 } 404 405 @Test 406 fun dump_onUnlockIntentWhenBiometricEnrolled_invalidNum_noArrayOutOfBoundsException() { 407 // GIVEN an invalid input (-1) 408 secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, 409 "-1", currentUser) 410 411 // WHEN the setting updates 412 updateSetting(secureSettings.getUriFor( 413 ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED 414 )) 415 416 // THEN no exception thrown 417 activeUnlockConfig.dump(mockPrintWriter, emptyArray()) 418 } 419 420 private fun updateSetting(uri: Uri) { 421 verifyRegisterSettingObserver() 422 settingsObserverCaptor.value.onChange( 423 false, 424 listOf(uri), 425 0, 426 0 /* flags */ 427 ) 428 } 429 430 private fun verifyRegisterSettingObserver() { 431 verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)) 432 verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT)) 433 verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)) 434 verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS)) 435 verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO)) 436 verifyRegisterSettingObserver(secureSettings.getUriFor( 437 ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED 438 )) 439 verifyRegisterSettingObserver(secureSettings.getUriFor( 440 ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS 441 )) 442 } 443 444 private fun verifyRegisterSettingObserver(uri: Uri) { 445 verify(contentResolver).registerContentObserver( 446 eq(uri), 447 eq(false), 448 capture(settingsObserverCaptor), 449 eq(UserHandle.USER_ALL)) 450 } 451 } 452