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.systemui.statusbar.policy 18 19 import android.content.pm.PackageManager 20 import android.hardware.camera2.CameraCharacteristics 21 import android.hardware.camera2.CameraManager 22 import android.hardware.camera2.impl.CameraMetadataNative 23 import androidx.test.ext.junit.runners.AndroidJUnit4 24 import androidx.test.filters.SmallTest 25 import com.android.systemui.SysuiTestCase 26 import com.android.systemui.broadcast.BroadcastSender 27 import com.android.systemui.dump.DumpManager 28 import com.android.systemui.util.concurrency.FakeExecutor 29 import com.android.systemui.util.mockito.argumentCaptor 30 import com.android.systemui.util.mockito.capture 31 import com.android.systemui.util.settings.FakeSettings 32 import com.android.systemui.util.time.FakeSystemClock 33 import java.util.concurrent.Executor 34 import org.junit.Before 35 import org.junit.Test 36 import org.junit.runner.RunWith 37 import org.mockito.Mock 38 import org.mockito.Mockito.any 39 import org.mockito.Mockito.clearInvocations 40 import org.mockito.Mockito.eq 41 import org.mockito.Mockito.never 42 import org.mockito.Mockito.verify 43 import org.mockito.Mockito.verifyNoMoreInteractions 44 import org.mockito.Mockito.`when` 45 import org.mockito.MockitoAnnotations 46 47 @SmallTest 48 @RunWith(AndroidJUnit4::class) 49 class FlashlightControllerImplTest : SysuiTestCase() { 50 51 @Mock 52 private lateinit var dumpManager: DumpManager 53 54 @Mock 55 private lateinit var cameraManager: CameraManager 56 57 @Mock 58 private lateinit var broadcastSender: BroadcastSender 59 60 @Mock 61 private lateinit var packageManager: PackageManager 62 63 private lateinit var fakeSettings: FakeSettings 64 private lateinit var fakeSystemClock: FakeSystemClock 65 private lateinit var backgroundExecutor: FakeExecutor 66 private lateinit var controller: FlashlightControllerImpl 67 68 @Before setupnull69 fun setup() { 70 MockitoAnnotations.initMocks(this) 71 72 fakeSystemClock = FakeSystemClock() 73 backgroundExecutor = FakeExecutor(fakeSystemClock) 74 fakeSettings = FakeSettings() 75 76 `when`(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) 77 .thenReturn(true) 78 79 controller = FlashlightControllerImpl( 80 dumpManager, 81 cameraManager, 82 backgroundExecutor, 83 fakeSettings, 84 broadcastSender, 85 packageManager 86 ) 87 } 88 89 @Test testNoCameraManagerInteractionDirectlyOnConstructornull90 fun testNoCameraManagerInteractionDirectlyOnConstructor() { 91 verifyNoMoreInteractions(cameraManager) 92 } 93 94 @Test testCameraManagerInitAfterConstructionOnExecutornull95 fun testCameraManagerInitAfterConstructionOnExecutor() { 96 injectCamera() 97 backgroundExecutor.runAllReady() 98 99 verify(cameraManager).registerTorchCallback(eq(backgroundExecutor), any()) 100 } 101 102 @Test testNoCallbackIfNoFlashCameranull103 fun testNoCallbackIfNoFlashCamera() { 104 injectCamera(flash = false) 105 backgroundExecutor.runAllReady() 106 107 verify(cameraManager, never()).registerTorchCallback(any<Executor>(), any()) 108 } 109 110 @Test testNoCallbackIfNoBackCameranull111 fun testNoCallbackIfNoBackCamera() { 112 injectCamera(facing = CameraCharacteristics.LENS_FACING_FRONT) 113 backgroundExecutor.runAllReady() 114 115 verify(cameraManager, never()).registerTorchCallback(any<Executor>(), any()) 116 } 117 118 @Test testSetFlashlightInBackgroundExecutornull119 fun testSetFlashlightInBackgroundExecutor() { 120 val id = injectCamera() 121 backgroundExecutor.runAllReady() 122 123 clearInvocations(cameraManager) 124 val enable = !controller.isEnabled 125 controller.setFlashlight(enable) 126 verifyNoMoreInteractions(cameraManager) 127 128 backgroundExecutor.runAllReady() 129 verify(cameraManager).setTorchMode(id, enable) 130 } 131 132 @Test testCallbackRemovedWhileDispatching_doesntCrashnull133 fun testCallbackRemovedWhileDispatching_doesntCrash() { 134 injectCamera() 135 var remove = false 136 val callback = object : FlashlightController.FlashlightListener { 137 override fun onFlashlightChanged(enabled: Boolean) { 138 if (remove) { 139 controller.removeCallback(this) 140 } 141 } 142 143 override fun onFlashlightError() {} 144 145 override fun onFlashlightAvailabilityChanged(available: Boolean) {} 146 } 147 controller.addCallback(callback) 148 controller.addCallback(object : FlashlightController.FlashlightListener { 149 override fun onFlashlightChanged(enabled: Boolean) {} 150 151 override fun onFlashlightError() {} 152 153 override fun onFlashlightAvailabilityChanged(available: Boolean) {} 154 }) 155 backgroundExecutor.runAllReady() 156 157 val captor = argumentCaptor<CameraManager.TorchCallback>() 158 verify(cameraManager).registerTorchCallback(any(), capture(captor)) 159 remove = true 160 captor.value.onTorchModeChanged(ID, true) 161 } 162 injectCameranull163 private fun injectCamera( 164 flash: Boolean = true, 165 facing: Int = CameraCharacteristics.LENS_FACING_BACK 166 ): String { 167 val cameraID = ID 168 val camera = CameraCharacteristics(CameraMetadataNative().apply { 169 set(CameraCharacteristics.FLASH_INFO_AVAILABLE, flash) 170 set(CameraCharacteristics.LENS_FACING, facing) 171 }) 172 `when`(cameraManager.cameraIdList).thenReturn(arrayOf(cameraID)) 173 `when`(cameraManager.getCameraCharacteristics(cameraID)).thenReturn(camera) 174 return cameraID 175 } 176 177 companion object { 178 private const val ID = "ID" 179 } 180 } 181