1 package com.android.systemui.qs 2 3 import android.content.BroadcastReceiver 4 import android.content.Context 5 import android.content.Intent 6 import android.content.IntentFilter 7 import android.permission.PermissionManager 8 import android.safetycenter.SafetyCenterManager 9 import android.testing.AndroidTestingRunner 10 import android.view.View 11 import androidx.test.filters.SmallTest 12 import com.android.internal.logging.UiEventLogger 13 import com.android.systemui.SysuiTestCase 14 import com.android.systemui.appops.AppOpsController 15 import com.android.systemui.broadcast.BroadcastDispatcher 16 import com.android.systemui.flags.FeatureFlags 17 import com.android.systemui.flags.Flags 18 import com.android.systemui.plugins.ActivityStarter 19 import com.android.systemui.privacy.OngoingPrivacyChip 20 import com.android.systemui.privacy.PrivacyDialogController 21 import com.android.systemui.privacy.PrivacyDialogControllerV2 22 import com.android.systemui.privacy.PrivacyItemController 23 import com.android.systemui.privacy.logging.PrivacyLogger 24 import com.android.systemui.statusbar.phone.StatusIconContainer 25 import com.android.systemui.statusbar.policy.DeviceProvisionedController 26 import com.android.systemui.util.concurrency.FakeExecutor 27 import com.android.systemui.util.mockito.any 28 import com.android.systemui.util.mockito.argumentCaptor 29 import com.android.systemui.util.mockito.capture 30 import com.android.systemui.util.mockito.eq 31 import com.android.systemui.util.mockito.nullable 32 import com.android.systemui.util.time.FakeSystemClock 33 import org.junit.Before 34 import org.junit.Test 35 import org.junit.runner.RunWith 36 import org.mockito.ArgumentMatchers.anyInt 37 import org.mockito.Mock 38 import org.mockito.Mockito 39 import org.mockito.Mockito.never 40 import org.mockito.Mockito.times 41 import org.mockito.Mockito.verify 42 import org.mockito.MockitoAnnotations 43 import org.mockito.Mockito.`when` as whenever 44 45 private fun <T> eq(value: T): T = Mockito.eq(value) ?: value 46 private fun <T> any(): T = Mockito.any<T>() 47 48 @SmallTest 49 @RunWith(AndroidTestingRunner::class) 50 class HeaderPrivacyIconsControllerTest : SysuiTestCase() { 51 52 @Mock 53 private lateinit var privacyItemController: PrivacyItemController 54 @Mock 55 private lateinit var uiEventLogger: UiEventLogger 56 @Mock 57 private lateinit var privacyChip: OngoingPrivacyChip 58 @Mock 59 private lateinit var privacyDialogController: PrivacyDialogController 60 @Mock 61 private lateinit var privacyDialogControllerV2: PrivacyDialogControllerV2 62 @Mock 63 private lateinit var privacyLogger: PrivacyLogger 64 @Mock 65 private lateinit var iconContainer: StatusIconContainer 66 @Mock 67 private lateinit var permissionManager: PermissionManager 68 @Mock 69 private lateinit var activityStarter: ActivityStarter 70 @Mock 71 private lateinit var appOpsController: AppOpsController 72 @Mock 73 private lateinit var broadcastDispatcher: BroadcastDispatcher 74 @Mock 75 private lateinit var safetyCenterManager: SafetyCenterManager 76 @Mock 77 private lateinit var deviceProvisionedController: DeviceProvisionedController 78 @Mock 79 private lateinit var featureFlags: FeatureFlags 80 81 private val uiExecutor = FakeExecutor(FakeSystemClock()) 82 private val backgroundExecutor = FakeExecutor(FakeSystemClock()) 83 private lateinit var cameraSlotName: String 84 private lateinit var microphoneSlotName: String 85 private lateinit var locationSlotName: String 86 private lateinit var controller: HeaderPrivacyIconsController 87 88 @Before 89 fun setUp() { 90 MockitoAnnotations.initMocks(this) 91 whenever(privacyChip.context).thenReturn(context) 92 whenever(privacyChip.resources).thenReturn(context.resources) 93 whenever(privacyChip.isAttachedToWindow).thenReturn(true) 94 whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) 95 96 cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera) 97 microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone) 98 locationSlotName = context.getString(com.android.internal.R.string.status_bar_location) 99 100 controller = HeaderPrivacyIconsController( 101 privacyItemController, 102 uiEventLogger, 103 privacyChip, 104 privacyDialogController, 105 privacyDialogControllerV2, 106 privacyLogger, 107 iconContainer, 108 permissionManager, 109 backgroundExecutor, 110 uiExecutor, 111 activityStarter, 112 appOpsController, 113 broadcastDispatcher, 114 safetyCenterManager, 115 deviceProvisionedController, 116 featureFlags 117 ) 118 119 backgroundExecutor.runAllReady() 120 } 121 122 @Test 123 fun testIgnoredSlotsOnParentVisible_noIndicators() { 124 setPrivacyController(micCamera = false, location = false) 125 126 controller.onParentVisible() 127 128 verify(iconContainer).removeIgnoredSlot(cameraSlotName) 129 verify(iconContainer).removeIgnoredSlot(microphoneSlotName) 130 verify(iconContainer).removeIgnoredSlot(locationSlotName) 131 } 132 133 @Test 134 fun testIgnoredSlotsOnParentVisible_onlyMicCamera() { 135 setPrivacyController(micCamera = true, location = false) 136 137 controller.onParentVisible() 138 139 verify(iconContainer).addIgnoredSlot(cameraSlotName) 140 verify(iconContainer).addIgnoredSlot(microphoneSlotName) 141 verify(iconContainer).removeIgnoredSlot(locationSlotName) 142 } 143 144 @Test 145 fun testIgnoredSlotsOnParentVisible_onlyLocation() { 146 setPrivacyController(micCamera = false, location = true) 147 148 controller.onParentVisible() 149 150 verify(iconContainer).removeIgnoredSlot(cameraSlotName) 151 verify(iconContainer).removeIgnoredSlot(microphoneSlotName) 152 verify(iconContainer).addIgnoredSlot(locationSlotName) 153 } 154 155 @Test 156 fun testIgnoredSlotsOnParentVisible_locationMicCamera() { 157 setPrivacyController(micCamera = true, location = true) 158 159 controller.onParentVisible() 160 161 verify(iconContainer).addIgnoredSlot(cameraSlotName) 162 verify(iconContainer).addIgnoredSlot(microphoneSlotName) 163 verify(iconContainer).addIgnoredSlot(locationSlotName) 164 } 165 166 @Test 167 fun testPrivacyChipClickedWhenNewDialogDisabledAndSafetyCenterDisabled() { 168 whenever(featureFlags.isEnabled(Flags.ENABLE_NEW_PRIVACY_DIALOG)).thenReturn(false) 169 whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false) 170 controller.onParentVisible() 171 val captor = argumentCaptor<View.OnClickListener>() 172 verify(privacyChip).setOnClickListener(capture(captor)) 173 captor.value.onClick(privacyChip) 174 verify(privacyDialogController).showDialog(any(Context::class.java)) 175 verify(privacyDialogControllerV2, never()) 176 .showDialog(any(Context::class.java), any(View::class.java)) 177 } 178 179 @Test 180 fun testPrivacyChipClickedWhenNewDialogEnabledAndSafetyCenterDisabled() { 181 whenever(featureFlags.isEnabled(Flags.ENABLE_NEW_PRIVACY_DIALOG)).thenReturn(true) 182 whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false) 183 controller.onParentVisible() 184 val captor = argumentCaptor<View.OnClickListener>() 185 verify(privacyChip).setOnClickListener(capture(captor)) 186 captor.value.onClick(privacyChip) 187 verify(privacyDialogController).showDialog(any(Context::class.java)) 188 verify(privacyDialogControllerV2, never()) 189 .showDialog(any(Context::class.java), any(View::class.java)) 190 } 191 192 @Test 193 fun testPrivacyChipClickedWhenNewDialogDisabledAndSafetyCenterEnabled() { 194 whenever(featureFlags.isEnabled(Flags.ENABLE_NEW_PRIVACY_DIALOG)).thenReturn(false) 195 val receiverCaptor = argumentCaptor<BroadcastReceiver>() 196 whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true) 197 verify(broadcastDispatcher).registerReceiver(capture(receiverCaptor), 198 any(), any(), nullable(), anyInt(), nullable()) 199 receiverCaptor.value.onReceive( 200 context, 201 Intent(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED) 202 ) 203 backgroundExecutor.runAllReady() 204 controller.onParentVisible() 205 val captor = argumentCaptor<View.OnClickListener>() 206 verify(privacyChip).setOnClickListener(capture(captor)) 207 captor.value.onClick(privacyChip) 208 verify(privacyDialogController, never()).showDialog(any(Context::class.java)) 209 verify(privacyDialogControllerV2, never()) 210 .showDialog(any(Context::class.java), any(View::class.java)) 211 } 212 213 @Test 214 fun testPrivacyChipClickedWhenNewDialogEnabledAndSafetyCenterEnabled() { 215 whenever(featureFlags.isEnabled(Flags.ENABLE_NEW_PRIVACY_DIALOG)).thenReturn(true) 216 val receiverCaptor = argumentCaptor<BroadcastReceiver>() 217 whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true) 218 verify(broadcastDispatcher).registerReceiver(capture(receiverCaptor), 219 any(), any(), nullable(), anyInt(), nullable()) 220 receiverCaptor.value.onReceive( 221 context, 222 Intent(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED) 223 ) 224 backgroundExecutor.runAllReady() 225 controller.onParentVisible() 226 val captor = argumentCaptor<View.OnClickListener>() 227 verify(privacyChip).setOnClickListener(capture(captor)) 228 captor.value.onClick(privacyChip) 229 verify(privacyDialogControllerV2).showDialog(any(Context::class.java), eq(privacyChip)) 230 verify(privacyDialogController, never()).showDialog(any(Context::class.java)) 231 } 232 233 @Test 234 fun testBroadcastReceiverUnregisteredWhenChipDetached() { 235 whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false) 236 controller.attachStateChangeListener.onViewDetachedFromWindow(privacyChip) 237 backgroundExecutor.runAllReady() 238 val broadcastReceiverCaptor = argumentCaptor<BroadcastReceiver>() 239 verify(broadcastDispatcher).unregisterReceiver(capture(broadcastReceiverCaptor)) 240 } 241 242 @Test 243 fun testBroadcastReceiverRegisteredWhenChipAttached() { 244 whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false) 245 controller.attachStateChangeListener.onViewAttachedToWindow(privacyChip) 246 backgroundExecutor.runAllReady() 247 val broadcastReceiverCaptor = argumentCaptor<BroadcastReceiver>() 248 val intentFilterCaptor = argumentCaptor<IntentFilter>() 249 // Broadcast receiver is registered on init and when privacy chip is attached 250 verify(broadcastDispatcher, times(2)).registerReceiver( 251 capture(broadcastReceiverCaptor), 252 capture(intentFilterCaptor), any(), nullable(), anyInt(), nullable() 253 ) 254 } 255 256 @Test 257 fun testNoDialogWhenDeviceNotProvisioned() { 258 whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false) 259 controller.onParentVisible() 260 261 val captor = argumentCaptor<View.OnClickListener>() 262 verify(privacyChip).setOnClickListener(capture(captor)) 263 264 captor.value.onClick(privacyChip) 265 verify(privacyDialogController, never()).showDialog(any(Context::class.java)) 266 } 267 268 private fun setPrivacyController(micCamera: Boolean, location: Boolean) { 269 whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera) 270 whenever(privacyItemController.locationAvailable).thenReturn(location) 271 } 272 }