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