1 /* 2 * Copyright (C) 2020 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.controls.controller 18 19 import android.content.ComponentName 20 import android.content.Context 21 import android.os.Binder 22 import android.os.UserHandle 23 import android.service.controls.Control 24 import android.service.controls.DeviceTypes 25 import android.service.controls.IControlsSubscriber 26 import android.service.controls.IControlsSubscription 27 import androidx.test.ext.junit.runners.AndroidJUnit4 28 import androidx.test.filters.SmallTest 29 import com.android.systemui.SysuiTestCase 30 import com.android.systemui.settings.UserTracker 31 import com.android.systemui.util.concurrency.DelayableExecutor 32 import com.android.systemui.util.concurrency.FakeExecutor 33 import com.android.systemui.util.time.FakeSystemClock 34 import dagger.Lazy 35 import org.junit.After 36 import org.junit.Assert.assertEquals 37 import org.junit.Before 38 import org.junit.Test 39 import org.junit.runner.RunWith 40 import org.mockito.ArgumentCaptor 41 import org.mockito.Captor 42 import org.mockito.Mock 43 import org.mockito.Mockito 44 import org.mockito.Mockito.mock 45 import org.mockito.Mockito.never 46 import org.mockito.Mockito.times 47 import org.mockito.Mockito.verify 48 import org.mockito.Mockito.`when` 49 import org.mockito.MockitoAnnotations 50 51 @SmallTest 52 @RunWith(AndroidJUnit4::class) 53 class ControlsBindingControllerImplTest : SysuiTestCase() { 54 55 companion object { capturenull56 fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() 57 fun <T> any(): T = Mockito.any<T>() 58 private val TEST_COMPONENT_NAME_1 = ComponentName("TEST_PKG", "TEST_CLS_1") 59 private val TEST_COMPONENT_NAME_2 = ComponentName("TEST_PKG", "TEST_CLS_2") 60 private val TEST_COMPONENT_NAME_3 = ComponentName("TEST_PKG", "TEST_CLS_3") 61 } 62 63 @Mock 64 private lateinit var mockControlsController: ControlsController 65 @Mock(stubOnly = true) 66 private lateinit var mockUserTracker: UserTracker 67 68 @Captor 69 private lateinit var subscriberCaptor: ArgumentCaptor<IControlsSubscriber.Stub> 70 71 @Captor 72 private lateinit var loadSubscriberCaptor: ArgumentCaptor<IControlsSubscriber.Stub> 73 74 @Captor 75 private lateinit var listStringCaptor: ArgumentCaptor<List<String>> 76 77 private val user = UserHandle.of(mContext.userId) 78 private val otherUser = UserHandle.of(user.identifier + 1) 79 80 private val executor = FakeExecutor(FakeSystemClock()) 81 private lateinit var controller: ControlsBindingController 82 private val providers = TestableControlsBindingControllerImpl.providers 83 84 @Before 85 fun setUp() { 86 MockitoAnnotations.initMocks(this) 87 providers.clear() 88 `when`(mockUserTracker.userHandle).thenReturn(user) 89 90 controller = TestableControlsBindingControllerImpl( 91 mContext, executor, Lazy { mockControlsController }, mockUserTracker) 92 } 93 94 @After tearDownnull95 fun tearDown() { 96 executor.advanceClockToLast() 97 executor.runAllReady() 98 } 99 100 @Test testStartOnUsernull101 fun testStartOnUser() { 102 assertEquals(user.identifier, controller.currentUserId) 103 } 104 105 @Test testBindAndLoadnull106 fun testBindAndLoad() { 107 val callback = object : ControlsBindingController.LoadCallback { 108 override fun error(message: String) {} 109 110 override fun accept(t: List<Control>) {} 111 } 112 controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) 113 114 verify(providers[0]).maybeBindAndLoad(any()) 115 } 116 117 @Test testBindAndLoad_cancelnull118 fun testBindAndLoad_cancel() { 119 val callback = mock(ControlsBindingController.LoadCallback::class.java) 120 val subscription = mock(IControlsSubscription::class.java) 121 122 val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) 123 124 verify(providers[0]).maybeBindAndLoad(capture(loadSubscriberCaptor)) 125 loadSubscriberCaptor.value.onSubscribe(Binder(), subscription) 126 127 canceller.run() 128 verify(providers[0]).cancelSubscription(subscription) 129 verify(callback).error(any()) 130 } 131 132 @Test testBindAndLoad_noCancelAfterOnCompletenull133 fun testBindAndLoad_noCancelAfterOnComplete() { 134 val callback = object : ControlsBindingController.LoadCallback { 135 override fun error(message: String) {} 136 137 override fun accept(t: List<Control>) {} 138 } 139 val subscription = mock(IControlsSubscription::class.java) 140 141 val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) 142 143 verify(providers[0]).maybeBindAndLoad(capture(loadSubscriberCaptor)) 144 val b = Binder() 145 loadSubscriberCaptor.value.onSubscribe(b, subscription) 146 147 loadSubscriberCaptor.value.onComplete(b) 148 canceller.run() 149 verify(providers[0], never()).cancelSubscription(subscription) 150 } 151 152 @Test testLoad_onCompleteRemovesTimeoutnull153 fun testLoad_onCompleteRemovesTimeout() { 154 val callback = object : ControlsBindingController.LoadCallback { 155 override fun error(message: String) {} 156 157 override fun accept(t: List<Control>) {} 158 } 159 val subscription = mock(IControlsSubscription::class.java) 160 161 val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) 162 163 verify(providers[0]).maybeBindAndLoad(capture(subscriberCaptor)) 164 val b = Binder() 165 subscriberCaptor.value.onSubscribe(b, subscription) 166 167 subscriberCaptor.value.onComplete(b) 168 verify(providers[0]).cancelLoadTimeout() 169 } 170 171 @Test testLoad_onErrorRemovesTimeoutnull172 fun testLoad_onErrorRemovesTimeout() { 173 val callback = object : ControlsBindingController.LoadCallback { 174 override fun error(message: String) {} 175 176 override fun accept(t: List<Control>) {} 177 } 178 val subscription = mock(IControlsSubscription::class.java) 179 180 val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) 181 182 verify(providers[0]).maybeBindAndLoad(capture(subscriberCaptor)) 183 val b = Binder() 184 subscriberCaptor.value.onSubscribe(b, subscription) 185 186 subscriberCaptor.value.onError(b, "") 187 verify(providers[0]).cancelLoadTimeout() 188 } 189 190 @Test testBindAndLoad_noCancelAfterOnErrornull191 fun testBindAndLoad_noCancelAfterOnError() { 192 val callback = object : ControlsBindingController.LoadCallback { 193 override fun error(message: String) {} 194 195 override fun accept(t: List<Control>) {} 196 } 197 val subscription = mock(IControlsSubscription::class.java) 198 199 val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) 200 201 verify(providers[0]).maybeBindAndLoad(capture(loadSubscriberCaptor)) 202 val b = Binder() 203 loadSubscriberCaptor.value.onSubscribe(b, subscription) 204 205 loadSubscriberCaptor.value.onError(b, "") 206 canceller.run() 207 verify(providers[0], never()).cancelSubscription(subscription) 208 } 209 210 @Test testBindAndLoadSuggestednull211 fun testBindAndLoadSuggested() { 212 val callback = object : ControlsBindingController.LoadCallback { 213 override fun error(message: String) {} 214 215 override fun accept(t: List<Control>) {} 216 } 217 controller.bindAndLoadSuggested(TEST_COMPONENT_NAME_1, callback) 218 219 verify(providers[0]).maybeBindAndLoadSuggested(any()) 220 } 221 222 @Test testLoadSuggested_onCompleteRemovesTimeoutnull223 fun testLoadSuggested_onCompleteRemovesTimeout() { 224 val callback = object : ControlsBindingController.LoadCallback { 225 override fun error(message: String) {} 226 227 override fun accept(t: List<Control>) {} 228 } 229 val subscription = mock(IControlsSubscription::class.java) 230 231 controller.bindAndLoadSuggested(TEST_COMPONENT_NAME_1, callback) 232 233 verify(providers[0]).maybeBindAndLoadSuggested(capture(subscriberCaptor)) 234 val b = Binder() 235 subscriberCaptor.value.onSubscribe(b, subscription) 236 237 subscriberCaptor.value.onComplete(b) 238 verify(providers[0]).cancelLoadTimeout() 239 } 240 241 @Test testLoadSuggested_onErrorRemovesTimeoutnull242 fun testLoadSuggested_onErrorRemovesTimeout() { 243 val callback = object : ControlsBindingController.LoadCallback { 244 override fun error(message: String) {} 245 246 override fun accept(t: List<Control>) {} 247 } 248 val subscription = mock(IControlsSubscription::class.java) 249 250 controller.bindAndLoadSuggested(TEST_COMPONENT_NAME_1, callback) 251 252 verify(providers[0]).maybeBindAndLoadSuggested(capture(subscriberCaptor)) 253 val b = Binder() 254 subscriberCaptor.value.onSubscribe(b, subscription) 255 256 subscriberCaptor.value.onError(b, "") 257 verify(providers[0]).cancelLoadTimeout() 258 } 259 260 @Test testBindServicenull261 fun testBindService() { 262 controller.bindService(TEST_COMPONENT_NAME_1) 263 executor.runAllReady() 264 265 verify(providers[0]).bindService() 266 } 267 268 @Test testBindServiceForPanelnull269 fun testBindServiceForPanel() { 270 controller.bindServiceForPanel(TEST_COMPONENT_NAME_1) 271 executor.runAllReady() 272 273 verify(providers[0]).bindServiceForPanel() 274 } 275 276 @Test testSubscribenull277 fun testSubscribe() { 278 val controlInfo1 = ControlInfo("id_1", "", "", DeviceTypes.TYPE_UNKNOWN) 279 val controlInfo2 = ControlInfo("id_2", "", "", DeviceTypes.TYPE_UNKNOWN) 280 val structure = 281 StructureInfo(TEST_COMPONENT_NAME_1, "Home", listOf(controlInfo1, controlInfo2)) 282 283 controller.subscribe(structure) 284 285 executor.runAllReady() 286 287 val subs = mock(IControlsSubscription::class.java) 288 verify(providers[0]).maybeBindAndSubscribe( 289 capture(listStringCaptor), capture(subscriberCaptor)) 290 assertEquals(listStringCaptor.value, 291 listOf(controlInfo1.controlId, controlInfo2.controlId)) 292 293 subscriberCaptor.value.onSubscribe(providers[0].token, subs) 294 } 295 296 @Test testUnsubscribe_notRefreshingnull297 fun testUnsubscribe_notRefreshing() { 298 controller.bindService(TEST_COMPONENT_NAME_2) 299 controller.unsubscribe() 300 301 executor.runAllReady() 302 303 verify(providers[0], never()).cancelSubscription(any()) 304 } 305 306 @Test testUnsubscribe_refreshingnull307 fun testUnsubscribe_refreshing() { 308 val controlInfo1 = ControlInfo("id_1", "", "", DeviceTypes.TYPE_UNKNOWN) 309 val controlInfo2 = ControlInfo("id_2", "", "", DeviceTypes.TYPE_UNKNOWN) 310 val structure = 311 StructureInfo(TEST_COMPONENT_NAME_1, "Home", listOf(controlInfo1, controlInfo2)) 312 313 controller.subscribe(structure) 314 executor.runAllReady() 315 316 val subs = mock(IControlsSubscription::class.java) 317 verify(providers[0]).maybeBindAndSubscribe( 318 capture(listStringCaptor), capture(subscriberCaptor)) 319 assertEquals(listStringCaptor.value, 320 listOf(controlInfo1.controlId, controlInfo2.controlId)) 321 322 subscriberCaptor.value.onSubscribe(providers[0].token, subs) 323 executor.runAllReady() 324 325 controller.unsubscribe() 326 executor.runAllReady() 327 328 verify(providers[0]).cancelSubscription(subs) 329 } 330 331 @Test testCurrentUserIdnull332 fun testCurrentUserId() { 333 controller.changeUser(otherUser) 334 assertEquals(otherUser.identifier, controller.currentUserId) 335 } 336 337 @Test testChangeUsers_providersHaveCorrectUsernull338 fun testChangeUsers_providersHaveCorrectUser() { 339 controller.bindService(TEST_COMPONENT_NAME_1) 340 assertEquals(user, providers[0].user) 341 342 controller.changeUser(otherUser) 343 344 controller.bindService(TEST_COMPONENT_NAME_2) 345 assertEquals(otherUser, providers[0].user) 346 } 347 348 @Test testChangeUsers_providersUnboundnull349 fun testChangeUsers_providersUnbound() { 350 controller.bindService(TEST_COMPONENT_NAME_1) 351 controller.changeUser(otherUser) 352 353 verify(providers[0]).unbindService() 354 355 controller.bindService(TEST_COMPONENT_NAME_2) 356 controller.changeUser(user) 357 358 verify(providers[0]).unbindService() 359 } 360 361 @Test testComponentRemoved_existingIsUnboundnull362 fun testComponentRemoved_existingIsUnbound() { 363 controller.bindService(TEST_COMPONENT_NAME_1) 364 365 controller.onComponentRemoved(TEST_COMPONENT_NAME_1) 366 367 executor.runAllReady() 368 369 verify(providers[0], times(1)).unbindService() 370 } 371 } 372 373 class TestableControlsBindingControllerImpl( 374 context: Context, 375 executor: DelayableExecutor, 376 lazyController: Lazy<ControlsController>, 377 userTracker: UserTracker 378 ) : ControlsBindingControllerImpl( 379 context, 380 executor, 381 lazyController, 382 mock(PackageUpdateMonitor.Factory::class.java), 383 userTracker 384 ) { 385 386 companion object { 387 val providers = mutableListOf<ControlsProviderLifecycleManager>() 388 } 389 390 // Replaces the real provider with a mock and puts the mock in a visible set. 391 // The mock has the same componentName and user as the real one would have createProviderManagernull392 override fun createProviderManager(component: ComponentName): 393 ControlsProviderLifecycleManager { 394 val realProvider = super.createProviderManager(component) 395 val provider = mock(ControlsProviderLifecycleManager::class.java) 396 val token = Binder() 397 `when`(provider.componentName).thenReturn(realProvider.componentName) 398 `when`(provider.token).thenReturn(token) 399 `when`(provider.user).thenReturn(realProvider.user) 400 401 providers.clear() 402 providers.add(provider) 403 404 return provider 405 } 406 } 407