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