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