<lambda>null1 package com.android.systemui.screenshot
2 
3 import android.content.ComponentName
4 import android.graphics.Bitmap
5 import android.net.Uri
6 import android.testing.AndroidTestingRunner
7 import android.view.Display
8 import android.view.Display.TYPE_EXTERNAL
9 import android.view.Display.TYPE_INTERNAL
10 import android.view.Display.TYPE_OVERLAY
11 import android.view.Display.TYPE_VIRTUAL
12 import android.view.Display.TYPE_WIFI
13 import android.view.WindowManager
14 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
15 import androidx.test.filters.SmallTest
16 import com.android.internal.logging.testing.UiEventLoggerFake
17 import com.android.internal.util.ScreenshotRequest
18 import com.android.systemui.SysuiTestCase
19 import com.android.systemui.display.data.repository.FakeDisplayRepository
20 import com.android.systemui.display.data.repository.display
21 import com.android.systemui.util.mockito.any
22 import com.android.systemui.util.mockito.eq
23 import com.android.systemui.util.mockito.kotlinArgumentCaptor as ArgumentCaptor
24 import com.android.systemui.util.mockito.mock
25 import com.android.systemui.util.mockito.whenever
26 import com.google.common.truth.Truth.assertThat
27 import java.lang.IllegalStateException
28 import java.util.function.Consumer
29 import kotlinx.coroutines.test.TestScope
30 import kotlinx.coroutines.test.UnconfinedTestDispatcher
31 import kotlinx.coroutines.test.runCurrent
32 import kotlinx.coroutines.test.runTest
33 import org.junit.Before
34 import org.junit.Test
35 import org.junit.runner.RunWith
36 import org.mockito.Mockito.never
37 import org.mockito.Mockito.times
38 import org.mockito.Mockito.verify
39 import org.mockito.Mockito.verifyNoMoreInteractions
40 
41 @RunWith(AndroidTestingRunner::class)
42 @SmallTest
43 class TakeScreenshotExecutorTest : SysuiTestCase() {
44 
45     private val controller = mock<ScreenshotController>()
46     private val notificationsController0 = mock<ScreenshotNotificationsController>()
47     private val notificationsController1 = mock<ScreenshotNotificationsController>()
48     private val controllerFactory = mock<ScreenshotController.Factory>()
49     private val callback = mock<TakeScreenshotService.RequestCallback>()
50     private val notificationControllerFactory = mock<ScreenshotNotificationsController.Factory>()
51 
52     private val fakeDisplayRepository = FakeDisplayRepository()
53     private val requestProcessor = FakeRequestProcessor()
54     private val topComponent = ComponentName(mContext, TakeScreenshotExecutorTest::class.java)
55     private val testScope = TestScope(UnconfinedTestDispatcher())
56     private val eventLogger = UiEventLoggerFake()
57     private val headlessHandler = mock<HeadlessScreenshotHandler>()
58 
59     private val screenshotExecutor =
60         TakeScreenshotExecutorImpl(
61             controllerFactory,
62             fakeDisplayRepository,
63             testScope,
64             requestProcessor,
65             eventLogger,
66             notificationControllerFactory,
67             headlessHandler,
68         )
69 
70     @Before
71     fun setUp() {
72         whenever(controllerFactory.create(any(), any())).thenReturn(controller)
73         whenever(notificationControllerFactory.create(eq(0))).thenReturn(notificationsController0)
74         whenever(notificationControllerFactory.create(eq(1))).thenReturn(notificationsController1)
75     }
76 
77     @Test
78     fun executeScreenshots_severalDisplays_callsControllerForEachOne() =
79         testScope.runTest {
80             val internalDisplay = display(TYPE_INTERNAL, id = 0)
81             val externalDisplay = display(TYPE_EXTERNAL, id = 1)
82             setDisplays(internalDisplay, externalDisplay)
83             val onSaved = { _: Uri? -> }
84             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
85 
86             verify(controllerFactory).create(eq(internalDisplay), any())
87             verify(controllerFactory, never()).create(eq(externalDisplay), any())
88 
89             val capturer = ArgumentCaptor<ScreenshotData>()
90 
91             verify(controller).handleScreenshot(capturer.capture(), any(), any())
92             assertThat(capturer.value.displayId).isEqualTo(0)
93             // OnSaved callback should be different.
94             verify(headlessHandler).handleScreenshot(capturer.capture(), any(), any())
95             assertThat(capturer.value.displayId).isEqualTo(1)
96 
97             assertThat(eventLogger.numLogs()).isEqualTo(2)
98             assertThat(eventLogger.get(0).eventId)
99                 .isEqualTo(ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER.id)
100             assertThat(eventLogger.get(0).packageName).isEqualTo(topComponent.packageName)
101             assertThat(eventLogger.get(1).eventId)
102                 .isEqualTo(ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER.id)
103             assertThat(eventLogger.get(1).packageName).isEqualTo(topComponent.packageName)
104 
105             screenshotExecutor.onDestroy()
106         }
107 
108     @Test
109     fun executeScreenshots_providedImageType_callsOnlyDefaultDisplayController() =
110         testScope.runTest {
111             val internalDisplay = display(TYPE_INTERNAL, id = 0)
112             val externalDisplay = display(TYPE_EXTERNAL, id = 1)
113             setDisplays(internalDisplay, externalDisplay)
114             val onSaved = { _: Uri? -> }
115             screenshotExecutor.executeScreenshots(
116                 createScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE),
117                 onSaved,
118                 callback
119             )
120 
121             verify(controllerFactory).create(eq(internalDisplay), any())
122             verify(controllerFactory, never()).create(eq(externalDisplay), any())
123 
124             val capturer = ArgumentCaptor<ScreenshotData>()
125 
126             verify(controller).handleScreenshot(capturer.capture(), any(), any())
127             assertThat(capturer.value.displayId).isEqualTo(0)
128             // OnSaved callback should be different.
129             verify(headlessHandler, never()).handleScreenshot(any(), any(), any())
130 
131             assertThat(eventLogger.numLogs()).isEqualTo(1)
132             assertThat(eventLogger.get(0).eventId)
133                 .isEqualTo(ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER.id)
134             assertThat(eventLogger.get(0).packageName).isEqualTo(topComponent.packageName)
135 
136             screenshotExecutor.onDestroy()
137         }
138 
139     @Test
140     fun executeScreenshots_onlyVirtualDisplays_noInteractionsWithControllers() =
141         testScope.runTest {
142             setDisplays(display(TYPE_VIRTUAL, id = 0), display(TYPE_VIRTUAL, id = 1))
143             val onSaved = { _: Uri? -> }
144             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
145 
146             verifyNoMoreInteractions(controllerFactory)
147             verify(headlessHandler, never()).handleScreenshot(any(), any(), any())
148             screenshotExecutor.onDestroy()
149         }
150 
151     @Test
152     fun executeScreenshots_allowedTypes_allCaptured() =
153         testScope.runTest {
154             whenever(controllerFactory.create(any(), any())).thenReturn(controller)
155 
156             setDisplays(
157                 display(TYPE_INTERNAL, id = 0),
158                 display(TYPE_EXTERNAL, id = 1),
159                 display(TYPE_OVERLAY, id = 2),
160                 display(TYPE_WIFI, id = 3)
161             )
162             val onSaved = { _: Uri? -> }
163             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
164 
165             verify(controller, times(1)).handleScreenshot(any(), any(), any())
166             verify(headlessHandler, times(3)).handleScreenshot(any(), any(), any())
167             screenshotExecutor.onDestroy()
168         }
169 
170     @Test
171     fun executeScreenshots_reportsOnFinishedOnlyWhenBothFinished() =
172         testScope.runTest {
173             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
174             val onSaved = { _: Uri? -> }
175             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
176 
177             val capturer0 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
178             val capturer1 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
179 
180             verify(controller).handleScreenshot(any(), any(), capturer0.capture())
181             verify(headlessHandler).handleScreenshot(any(), any(), capturer1.capture())
182 
183             verify(callback, never()).onFinish()
184 
185             capturer0.value.onFinish()
186 
187             verify(callback, never()).onFinish()
188 
189             capturer1.value.onFinish()
190 
191             verify(callback).onFinish()
192             screenshotExecutor.onDestroy()
193         }
194 
195     @Test
196     fun executeScreenshots_oneFinishesOtherFails_reportFailsOnlyAtTheEnd() =
197         testScope.runTest {
198             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
199             val onSaved = { _: Uri? -> }
200             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
201 
202             val capturer0 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
203             val capturer1 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
204 
205             verify(controller).handleScreenshot(any(), any(), capturer0.capture())
206             verify(headlessHandler).handleScreenshot(any(), any(), capturer1.capture())
207 
208             verify(callback, never()).onFinish()
209 
210             capturer0.value.onFinish()
211 
212             verify(callback, never()).onFinish()
213 
214             capturer1.value.reportError()
215 
216             verify(callback, never()).onFinish()
217             verify(callback).reportError()
218 
219             screenshotExecutor.onDestroy()
220         }
221 
222     @Test
223     fun executeScreenshots_allDisplaysFail_reportsFail() =
224         testScope.runTest {
225             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
226             val onSaved = { _: Uri? -> }
227             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
228 
229             val capturer0 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
230             val capturer1 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
231 
232             verify(controller).handleScreenshot(any(), any(), capturer0.capture())
233             verify(headlessHandler).handleScreenshot(any(), any(), capturer1.capture())
234 
235             verify(callback, never()).onFinish()
236 
237             capturer0.value.reportError()
238 
239             verify(callback, never()).onFinish()
240             verify(callback, never()).reportError()
241 
242             capturer1.value.reportError()
243 
244             verify(callback, never()).onFinish()
245             verify(callback).reportError()
246             screenshotExecutor.onDestroy()
247         }
248 
249     @Test
250     fun onDestroy_propagatedToControllers() =
251         testScope.runTest {
252             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
253             val onSaved = { _: Uri? -> }
254             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
255 
256             screenshotExecutor.onDestroy()
257             verify(controller).onDestroy()
258         }
259 
260     @Test
261     fun removeWindows_propagatedToController() =
262         testScope.runTest {
263             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
264             val onSaved = { _: Uri? -> }
265             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
266 
267             screenshotExecutor.removeWindows()
268             verify(controller).removeWindow()
269 
270             screenshotExecutor.onDestroy()
271         }
272 
273     @Test
274     fun onCloseSystemDialogsReceived_propagatedToController() =
275         testScope.runTest {
276             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
277             val onSaved = { _: Uri? -> }
278             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
279 
280             screenshotExecutor.onCloseSystemDialogsReceived()
281             verify(controller).requestDismissal(any())
282 
283             screenshotExecutor.onDestroy()
284         }
285 
286     @Test
287     fun onCloseSystemDialogsReceived_controllerHasPendingTransitions() =
288         testScope.runTest {
289             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
290             whenever(controller.isPendingSharedTransition).thenReturn(true)
291             val onSaved = { _: Uri? -> }
292             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
293 
294             screenshotExecutor.onCloseSystemDialogsReceived()
295             verify(controller, never()).requestDismissal(any())
296 
297             screenshotExecutor.onDestroy()
298         }
299 
300     @Test
301     fun executeScreenshots_controllerCalledWithRequestProcessorReturnValue() =
302         testScope.runTest {
303             setDisplays(display(TYPE_INTERNAL, id = 0))
304             val screenshotRequest = createScreenshotRequest()
305             val toBeReturnedByProcessor = ScreenshotData.forTesting()
306             requestProcessor.toReturn = toBeReturnedByProcessor
307 
308             val onSaved = { _: Uri? -> }
309             screenshotExecutor.executeScreenshots(screenshotRequest, onSaved, callback)
310 
311             assertThat(requestProcessor.processed)
312                 .isEqualTo(ScreenshotData.fromRequest(screenshotRequest))
313 
314             val capturer = ArgumentCaptor<ScreenshotData>()
315             verify(controller).handleScreenshot(capturer.capture(), any(), any())
316             assertThat(capturer.value).isEqualTo(toBeReturnedByProcessor)
317 
318             screenshotExecutor.onDestroy()
319         }
320 
321     @Test
322     fun executeScreenshots_errorFromProcessor_logsScreenshotRequested() =
323         testScope.runTest {
324             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
325             val onSaved = { _: Uri? -> }
326             requestProcessor.shouldThrowException = true
327 
328             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
329 
330             val screenshotRequested =
331                 eventLogger.logs.filter {
332                     it.eventId == ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER.id
333                 }
334             assertThat(screenshotRequested).hasSize(2)
335             screenshotExecutor.onDestroy()
336         }
337 
338     @Test
339     fun executeScreenshots_errorFromProcessor_logsUiError() =
340         testScope.runTest {
341             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
342             val onSaved = { _: Uri? -> }
343             requestProcessor.shouldThrowException = true
344 
345             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
346 
347             val screenshotRequested =
348                 eventLogger.logs.filter {
349                     it.eventId == ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED.id
350                 }
351             assertThat(screenshotRequested).hasSize(2)
352             screenshotExecutor.onDestroy()
353         }
354 
355     @Test
356     fun executeScreenshots_errorFromProcessorOnDefaultDisplay_showsErrorNotification() =
357         testScope.runTest {
358             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
359             val onSaved = { _: Uri? -> }
360             requestProcessor.shouldThrowException = true
361 
362             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
363 
364             verify(notificationsController0).notifyScreenshotError(any())
365             screenshotExecutor.onDestroy()
366         }
367 
368     @Test
369     fun executeScreenshots_errorFromProcessorOnSecondaryDisplay_showsErrorNotification() =
370         testScope.runTest {
371             setDisplays(display(TYPE_INTERNAL, id = 0))
372             val onSaved = { _: Uri? -> }
373             requestProcessor.shouldThrowException = true
374 
375             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
376 
377             verify(notificationsController0).notifyScreenshotError(any())
378             screenshotExecutor.onDestroy()
379         }
380 
381     @Test
382     fun executeScreenshots_errorFromScreenshotController_reportsRequested() =
383         testScope.runTest {
384             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
385             val onSaved = { _: Uri? -> }
386             whenever(controller.handleScreenshot(any(), any(), any()))
387                 .thenThrow(IllegalStateException::class.java)
388             whenever(headlessHandler.handleScreenshot(any(), any(), any()))
389                 .thenThrow(IllegalStateException::class.java)
390 
391             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
392 
393             val screenshotRequested =
394                 eventLogger.logs.filter {
395                     it.eventId == ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER.id
396                 }
397             assertThat(screenshotRequested).hasSize(2)
398             screenshotExecutor.onDestroy()
399         }
400 
401     @Test
402     fun executeScreenshots_errorFromScreenshotController_reportsError() =
403         testScope.runTest {
404             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
405             val onSaved = { _: Uri? -> }
406             whenever(controller.handleScreenshot(any(), any(), any()))
407                 .thenThrow(IllegalStateException::class.java)
408             whenever(headlessHandler.handleScreenshot(any(), any(), any()))
409                 .thenThrow(IllegalStateException::class.java)
410 
411             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
412 
413             val screenshotRequested =
414                 eventLogger.logs.filter {
415                     it.eventId == ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED.id
416                 }
417             assertThat(screenshotRequested).hasSize(2)
418             screenshotExecutor.onDestroy()
419         }
420 
421     @Test
422     fun executeScreenshots_errorFromScreenshotController_showsErrorNotification() =
423         testScope.runTest {
424             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
425             val onSaved = { _: Uri? -> }
426             whenever(controller.handleScreenshot(any(), any(), any()))
427                 .thenThrow(IllegalStateException::class.java)
428             whenever(headlessHandler.handleScreenshot(any(), any(), any()))
429                 .thenThrow(IllegalStateException::class.java)
430 
431             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
432 
433             verify(notificationsController0).notifyScreenshotError(any())
434             verify(notificationsController1).notifyScreenshotError(any())
435             screenshotExecutor.onDestroy()
436         }
437 
438     @Test
439     fun executeScreenshots_finisherCalledWithNullUri_succeeds() =
440         testScope.runTest {
441             setDisplays(display(TYPE_INTERNAL, id = 0))
442             var onSavedCallCount = 0
443             val onSaved: (Uri?) -> Unit = {
444                 assertThat(it).isNull()
445                 onSavedCallCount += 1
446             }
447             whenever(controller.handleScreenshot(any(), any(), any())).thenAnswer {
448                 (it.getArgument(1) as Consumer<Uri?>).accept(null)
449             }
450 
451             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
452             assertThat(onSavedCallCount).isEqualTo(1)
453 
454             screenshotExecutor.onDestroy()
455         }
456 
457     private suspend fun TestScope.setDisplays(vararg displays: Display) {
458         fakeDisplayRepository.emit(displays.toSet())
459         runCurrent()
460     }
461 
462     private fun createScreenshotRequest(type: Int = WindowManager.TAKE_SCREENSHOT_FULLSCREEN) =
463         ScreenshotRequest.Builder(type, WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER)
464             .setTopComponent(topComponent)
465             .also {
466                 if (type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
467                     it.setBitmap(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888))
468                 }
469             }
470             .build()
471 
472     private class FakeRequestProcessor : ScreenshotRequestProcessor {
473         var processed: ScreenshotData? = null
474         var toReturn: ScreenshotData? = null
475         var shouldThrowException = false
476 
477         override suspend fun process(screenshot: ScreenshotData): ScreenshotData {
478             if (shouldThrowException) throw RequestProcessorException("")
479             processed = screenshot
480             return toReturn ?: screenshot
481         }
482     }
483 }
484