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