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.screenshot
18 
19 import android.content.ComponentName
20 import android.content.pm.ActivityInfo
21 import android.content.pm.PackageManager
22 import android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS
23 import android.testing.AndroidTestingRunner
24 import android.view.Display
25 import android.view.IWindowManager
26 import android.view.WindowManager
27 import androidx.test.filters.SmallTest
28 import com.android.systemui.util.mockito.any
29 import com.android.systemui.util.mockito.argThat
30 import com.android.systemui.util.mockito.eq
31 import com.android.systemui.util.mockito.whenever
32 import junit.framework.Assert.assertEquals
33 import junit.framework.Assert.assertTrue
34 import org.junit.Before
35 import org.junit.Test
36 import org.junit.runner.RunWith
37 import org.mockito.ArgumentMatcher
38 import org.mockito.Mock
39 import org.mockito.Mockito.mock
40 import org.mockito.Mockito.never
41 import org.mockito.Mockito.verify
42 import org.mockito.MockitoAnnotations
43 
44 @SmallTest
45 @RunWith(AndroidTestingRunner::class)
46 class ScreenshotDetectionControllerTest {
47 
48     @Mock lateinit var windowManager: IWindowManager
49 
50     @Mock lateinit var packageManager: PackageManager
51 
52     lateinit var controller: ScreenshotDetectionController
53 
54     @Before
55     fun setup() {
56         MockitoAnnotations.initMocks(this)
57 
58         controller = ScreenshotDetectionController(windowManager, packageManager)
59     }
60 
61     @Test
62     fun testMaybeNotifyOfScreenshot_ignoresOverview() {
63         val data = ScreenshotData.forTesting()
64         data.source = WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW
65 
66         val list = controller.maybeNotifyOfScreenshot(data)
67 
68         assertTrue(list.isEmpty())
69         verify(windowManager, never()).notifyScreenshotListeners(any())
70     }
71 
72     @Test
73     fun testMaybeNotifyOfScreenshot_emptySet() {
74         val data = ScreenshotData.forTesting()
75         data.source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
76 
77         whenever(windowManager.notifyScreenshotListeners(eq(Display.DEFAULT_DISPLAY)))
78             .thenReturn(listOf())
79 
80         val list = controller.maybeNotifyOfScreenshot(data)
81 
82         assertTrue(list.isEmpty())
83     }
84 
85     @Test
86     fun testMaybeNotifyOfScreenshot_oneApp() {
87         val data = ScreenshotData.forTesting()
88         data.source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
89 
90         val component = ComponentName("package1", "class1")
91         val appName = "app name"
92         val activityInfo = mock(ActivityInfo::class.java)
93 
94         whenever(
95                 packageManager.getActivityInfo(
96                     eq(component),
97                     any(PackageManager.ComponentInfoFlags::class.java)
98                 )
99             )
100             .thenReturn(activityInfo)
101         whenever(activityInfo.loadLabel(eq(packageManager))).thenReturn(appName)
102 
103         whenever(windowManager.notifyScreenshotListeners(eq(Display.DEFAULT_DISPLAY)))
104             .thenReturn(listOf(component))
105 
106         val list = controller.maybeNotifyOfScreenshot(data)
107 
108         assertEquals(1, list.size)
109         assertEquals(appName, list[0])
110     }
111 
112     @Test
113     fun testMaybeNotifyOfScreenshot_multipleApps() {
114         val data = ScreenshotData.forTesting()
115         data.source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
116 
117         val component1 = ComponentName("package1", "class1")
118         val component2 = ComponentName("package2", "class2")
119         val component3 = ComponentName("package3", "class3")
120         val appName1 = "app name 1"
121         val appName2 = "app name 2"
122         val appName3 = "app name 3"
123 
124         val activityInfo1 = mock(ActivityInfo::class.java)
125         val activityInfo2 = mock(ActivityInfo::class.java)
126         val activityInfo3 = mock(ActivityInfo::class.java)
127 
128         whenever(
129                 packageManager.getActivityInfo(
130                     eq(component1),
131                     any(PackageManager.ComponentInfoFlags::class.java)
132                 )
133             )
134             .thenReturn(activityInfo1)
135         whenever(
136                 packageManager.getActivityInfo(
137                     eq(component2),
138                     any(PackageManager.ComponentInfoFlags::class.java)
139                 )
140             )
141             .thenReturn(activityInfo2)
142         whenever(
143                 packageManager.getActivityInfo(
144                     eq(component3),
145                     any(PackageManager.ComponentInfoFlags::class.java)
146                 )
147             )
148             .thenReturn(activityInfo3)
149 
150         whenever(activityInfo1.loadLabel(eq(packageManager))).thenReturn(appName1)
151         whenever(activityInfo2.loadLabel(eq(packageManager))).thenReturn(appName2)
152         whenever(activityInfo3.loadLabel(eq(packageManager))).thenReturn(appName3)
153 
154         whenever(windowManager.notifyScreenshotListeners(eq(Display.DEFAULT_DISPLAY)))
155             .thenReturn(listOf(component1, component2, component3))
156 
157         val list = controller.maybeNotifyOfScreenshot(data)
158 
159         assertEquals(3, list.size)
160         assertEquals(appName1, list[0])
161         assertEquals(appName2, list[1])
162         assertEquals(appName3, list[2])
163     }
164 
165     private fun includesFlagBits(@PackageManager.ComponentInfoFlagsBits mask: Int) =
166         ComponentInfoFlagMatcher(mask, mask)
167     private fun excludesFlagBits(@PackageManager.ComponentInfoFlagsBits mask: Int) =
168         ComponentInfoFlagMatcher(mask, 0)
169 
170     private class ComponentInfoFlagMatcher(
171         @PackageManager.ComponentInfoFlagsBits val mask: Int, val value: Int
172     ): ArgumentMatcher<PackageManager.ComponentInfoFlags> {
173         override fun matches(flags: PackageManager.ComponentInfoFlags): Boolean {
174             return (mask.toLong() and flags.value) == value.toLong()
175         }
176 
177         override fun toString(): String{
178             return "mask 0x%08x == 0x%08x".format(mask, value)
179         }
180     }
181 
182     @Test
183     fun testMaybeNotifyOfScreenshot_disabledApp() {
184         val data = ScreenshotData.forTesting()
185         data.source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
186 
187         val component = ComponentName("package1", "class1")
188         val appName = "app name"
189         val activityInfo = mock(ActivityInfo::class.java)
190 
191         whenever(
192             packageManager.getActivityInfo(
193                 eq(component),
194                 argThat(includesFlagBits(MATCH_DISABLED_COMPONENTS))
195             )
196         ).thenReturn(activityInfo);
197 
198         whenever(
199             packageManager.getActivityInfo(
200                 eq(component),
201                 argThat(excludesFlagBits(MATCH_DISABLED_COMPONENTS))
202             )
203         ).thenThrow(PackageManager.NameNotFoundException::class.java);
204 
205         whenever(windowManager.notifyScreenshotListeners(eq(Display.DEFAULT_DISPLAY)))
206             .thenReturn(listOf(component))
207 
208         whenever(activityInfo.loadLabel(eq(packageManager))).thenReturn(appName)
209 
210         val list = controller.maybeNotifyOfScreenshot(data)
211 
212         assertEquals(1, list.size)
213         assertEquals(appName, list[0])
214     }
215 
216 }
217