• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.IBinder
22 import android.os.UserHandle
23 import android.service.controls.Control
24 import android.service.controls.IControlsActionCallback
25 import android.service.controls.IControlsSubscriber
26 import android.service.controls.IControlsSubscription
27 import android.service.controls.actions.ControlAction
28 import android.util.Log
29 import com.android.internal.annotations.VisibleForTesting
30 import com.android.systemui.dagger.SysUISingleton
31 import com.android.systemui.dagger.qualifiers.Background
32 import com.android.systemui.settings.UserTracker
33 import com.android.systemui.util.concurrency.DelayableExecutor
34 import dagger.Lazy
35 import java.util.concurrent.atomic.AtomicBoolean
36 import javax.inject.Inject
37 
38 @SysUISingleton
39 @VisibleForTesting
40 open class ControlsBindingControllerImpl @Inject constructor(
41     private val context: Context,
42     @Background private val backgroundExecutor: DelayableExecutor,
43     private val lazyController: Lazy<ControlsController>,
44     userTracker: UserTracker
45 ) : ControlsBindingController {
46 
47     companion object {
48         private const val TAG = "ControlsBindingControllerImpl"
49         private const val MAX_CONTROLS_REQUEST = 100000L
50         private const val SUGGESTED_STRUCTURES = 6L
51         private const val SUGGESTED_CONTROLS_REQUEST =
52             ControlsControllerImpl.SUGGESTED_CONTROLS_PER_STRUCTURE * SUGGESTED_STRUCTURES
53 
54         private val emptyCallback = object : ControlsBindingController.LoadCallback {
acceptnull55             override fun accept(controls: List<Control>) {}
errornull56             override fun error(message: String) {}
57         }
58     }
59 
60     private var currentUser = userTracker.userHandle
61 
62     override val currentUserId: Int
63         get() = currentUser.identifier
64 
65     private var currentProvider: ControlsProviderLifecycleManager? = null
66 
67     /*
68      * Will track any active subscriber for subscribe/unsubscribe requests coming into
69      * this controller. Only one can be active at any time
70      */
71     private var statefulControlSubscriber: StatefulControlSubscriber? = null
72 
73     /*
74      * Will track any active load subscriber. Only one can be active at any time.
75      */
76     private var loadSubscriber: LoadSubscriber? = null
77 
78     private val actionCallbackService = object : IControlsActionCallback.Stub() {
acceptnull79         override fun accept(
80             token: IBinder,
81             controlId: String,
82             @ControlAction.ResponseResult response: Int
83         ) {
84             backgroundExecutor.execute(OnActionResponseRunnable(token, controlId, response))
85         }
86     }
87 
88     @VisibleForTesting
createProviderManagernull89     internal open fun createProviderManager(component: ComponentName):
90             ControlsProviderLifecycleManager {
91         return ControlsProviderLifecycleManager(
92                 context,
93                 backgroundExecutor,
94                 actionCallbackService,
95                 currentUser,
96                 component
97         )
98     }
99 
retrieveLifecycleManagernull100     private fun retrieveLifecycleManager(component: ComponentName):
101             ControlsProviderLifecycleManager {
102         if (currentProvider != null && currentProvider?.componentName != component) {
103             unbind()
104         }
105 
106         val provider = currentProvider ?: createProviderManager(component)
107         currentProvider = provider
108 
109         return provider
110     }
111 
bindAndLoadnull112     override fun bindAndLoad(
113         component: ComponentName,
114         callback: ControlsBindingController.LoadCallback
115     ): Runnable {
116         loadSubscriber?.loadCancel()
117 
118         val ls = LoadSubscriber(callback, MAX_CONTROLS_REQUEST)
119         loadSubscriber = ls
120 
121         retrieveLifecycleManager(component).maybeBindAndLoad(ls)
122         return ls.loadCancel()
123     }
124 
bindAndLoadSuggestednull125     override fun bindAndLoadSuggested(
126         component: ComponentName,
127         callback: ControlsBindingController.LoadCallback
128     ) {
129         loadSubscriber?.loadCancel()
130         val ls = LoadSubscriber(callback, SUGGESTED_CONTROLS_REQUEST)
131         loadSubscriber = ls
132 
133         retrieveLifecycleManager(component).maybeBindAndLoadSuggested(ls)
134     }
135 
subscribenull136     override fun subscribe(structureInfo: StructureInfo) {
137         // make sure this has happened. only allow one active subscription
138         unsubscribe()
139 
140         val provider = retrieveLifecycleManager(structureInfo.componentName)
141         val scs = StatefulControlSubscriber(
142             lazyController.get(),
143             provider,
144             backgroundExecutor,
145             MAX_CONTROLS_REQUEST
146         )
147         statefulControlSubscriber = scs
148         provider.maybeBindAndSubscribe(structureInfo.controls.map { it.controlId }, scs)
149     }
150 
unsubscribenull151     override fun unsubscribe() {
152         statefulControlSubscriber?.cancel()
153         statefulControlSubscriber = null
154     }
155 
actionnull156     override fun action(
157         componentName: ComponentName,
158         controlInfo: ControlInfo,
159         action: ControlAction
160     ) {
161         if (statefulControlSubscriber == null) {
162             Log.w(TAG, "No actions can occur outside of an active subscription. Ignoring.")
163         } else {
164             retrieveLifecycleManager(componentName)
165                 .maybeBindAndSendAction(controlInfo.controlId, action)
166         }
167     }
168 
bindServicenull169     override fun bindService(component: ComponentName) {
170         retrieveLifecycleManager(component).bindService()
171     }
172 
bindServiceForPanelnull173     override fun bindServiceForPanel(component: ComponentName) {
174         retrieveLifecycleManager(component).bindServiceForPanel()
175     }
176 
changeUsernull177     override fun changeUser(newUser: UserHandle) {
178         if (newUser == currentUser) return
179 
180         unbind()
181         currentUser = newUser
182     }
183 
unbindnull184     private fun unbind() {
185         unsubscribe()
186 
187         loadSubscriber?.loadCancel()
188         loadSubscriber = null
189 
190         currentProvider?.unbindService()
191         currentProvider = null
192     }
193 
onComponentRemovednull194     override fun onComponentRemoved(componentName: ComponentName) {
195         backgroundExecutor.execute {
196             currentProvider?.let {
197                 if (it.componentName == componentName) {
198                     unbind()
199                 }
200             }
201         }
202     }
203 
toStringnull204     override fun toString(): String {
205         return StringBuilder("  ControlsBindingController:\n").apply {
206             append("    currentUser=$currentUser\n")
207             append("    StatefulControlSubscriber=$statefulControlSubscriber")
208             append("    Providers=$currentProvider\n")
209         }.toString()
210     }
211 
212     private abstract inner class CallbackRunnable(val token: IBinder) : Runnable {
213         protected val provider: ControlsProviderLifecycleManager? = currentProvider
214 
runnull215         override fun run() {
216             if (provider == null) {
217                 Log.e(TAG, "No current provider set")
218                 return
219             }
220             if (provider.user != currentUser) {
221                 Log.e(TAG, "User ${provider.user} is not current user")
222                 return
223             }
224             if (token != provider.token) {
225                 Log.e(TAG, "Provider for token:$token does not exist anymore")
226                 return
227             }
228 
229             doRun()
230         }
231 
doRunnull232         abstract fun doRun()
233     }
234 
235     private inner class OnLoadRunnable(
236         token: IBinder,
237         val list: List<Control>,
238         val callback: ControlsBindingController.LoadCallback
239     ) : CallbackRunnable(token) {
240         override fun doRun() {
241             Log.d(TAG, "LoadSubscription: Complete and loading controls")
242             callback.accept(list)
243         }
244     }
245 
246     private inner class OnCancelAndLoadRunnable(
247         token: IBinder,
248         val list: List<Control>,
249         val subscription: IControlsSubscription,
250         val callback: ControlsBindingController.LoadCallback
251     ) : CallbackRunnable(token) {
doRunnull252         override fun doRun() {
253             Log.d(TAG, "LoadSubscription: Canceling and loading controls")
254             provider?.cancelSubscription(subscription)
255             callback.accept(list)
256         }
257     }
258 
259     private inner class OnSubscribeRunnable(
260         token: IBinder,
261         val subscription: IControlsSubscription,
262         val requestLimit: Long
263     ) : CallbackRunnable(token) {
doRunnull264         override fun doRun() {
265             Log.d(TAG, "LoadSubscription: Starting subscription")
266             provider?.startSubscription(subscription, requestLimit)
267         }
268     }
269 
270     private inner class OnActionResponseRunnable(
271         token: IBinder,
272         val controlId: String,
273         @ControlAction.ResponseResult val response: Int
274     ) : CallbackRunnable(token) {
doRunnull275         override fun doRun() {
276             provider?.let {
277                 lazyController.get().onActionResponse(it.componentName, controlId, response)
278             }
279         }
280     }
281 
282     private inner class OnLoadErrorRunnable(
283         token: IBinder,
284         val error: String,
285         val callback: ControlsBindingController.LoadCallback
286     ) : CallbackRunnable(token) {
doRunnull287         override fun doRun() {
288             callback.error(error)
289             provider?.let {
290                 Log.e(TAG, "onError receive from '${it.componentName}': $error")
291             }
292         }
293     }
294 
295     private inner class LoadSubscriber(
296         var callback: ControlsBindingController.LoadCallback,
297         val requestLimit: Long
298     ) : IControlsSubscriber.Stub() {
299         val loadedControls = ArrayList<Control>()
300         private var isTerminated = AtomicBoolean(false)
301         private var _loadCancelInternal: (() -> Unit)? = null
302         private lateinit var subscription: IControlsSubscription
303 
304         /**
305          * Potentially cancel a subscriber. The subscriber may also have terminated, in which case
306          * the request is ignored.
307          */
<lambda>null308         fun loadCancel() = Runnable {
309             _loadCancelInternal?.let {
310                 Log.d(TAG, "Canceling loadSubscribtion")
311                 it.invoke()
312             }
313         }
314 
onSubscribenull315         override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
316             subscription = subs
317             _loadCancelInternal = { currentProvider?.cancelSubscription(subscription) }
318             backgroundExecutor.execute(OnSubscribeRunnable(token, subs, requestLimit))
319         }
320 
onNextnull321         override fun onNext(token: IBinder, c: Control) {
322             backgroundExecutor.execute {
323                 if (isTerminated.get()) return@execute
324 
325                 loadedControls.add(c)
326 
327                 // Once we have reached our requestLimit, send a request to cancel, and immediately
328                 // load the results. Calls to onError() and onComplete() are not required after
329                 // cancel.
330                 if (loadedControls.size >= requestLimit) {
331                     maybeTerminateAndRun(
332                         OnCancelAndLoadRunnable(token, loadedControls, subscription, callback)
333                     )
334                 }
335             }
336         }
337 
onErrornull338         override fun onError(token: IBinder, s: String) {
339             maybeTerminateAndRun(OnLoadErrorRunnable(token, s, callback))
340         }
341 
onCompletenull342         override fun onComplete(token: IBinder) {
343             maybeTerminateAndRun(OnLoadRunnable(token, loadedControls, callback))
344         }
345 
maybeTerminateAndRunnull346         private fun maybeTerminateAndRun(postTerminateFn: Runnable) {
347             if (isTerminated.get()) return
348 
349             _loadCancelInternal = {}
350 
351             // Reassign the callback to clear references to other areas of code. Binders such as
352             // this may not be GC'd right away, so do not hold onto these references.
353             callback = emptyCallback
354             currentProvider?.cancelLoadTimeout()
355 
356             backgroundExecutor.execute {
357                 isTerminated.compareAndSet(false, true)
358                 postTerminateFn.run()
359             }
360         }
361     }
362 }
363 
364 private data class Key(val component: ComponentName, val user: UserHandle)
365