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 private val packageUpdateMonitorFactory: PackageUpdateMonitor.Factory, 45 userTracker: UserTracker, 46 ) : ControlsBindingController { 47 48 companion object { 49 private const val TAG = "ControlsBindingControllerImpl" 50 private const val MAX_CONTROLS_REQUEST = 100000L 51 private const val SUGGESTED_STRUCTURES = 6L 52 private const val SUGGESTED_CONTROLS_REQUEST = 53 ControlsControllerImpl.SUGGESTED_CONTROLS_PER_STRUCTURE * SUGGESTED_STRUCTURES 54 55 private val emptyCallback = object : ControlsBindingController.LoadCallback { acceptnull56 override fun accept(controls: List<Control>) {} errornull57 override fun error(message: String) {} 58 } 59 } 60 61 private var currentUser = userTracker.userHandle 62 63 override val currentUserId: Int 64 get() = currentUser.identifier 65 66 private var currentProvider: ControlsProviderLifecycleManager? = null 67 68 /* 69 * Will track any active subscriber for subscribe/unsubscribe requests coming into 70 * this controller. Only one can be active at any time 71 */ 72 private var statefulControlSubscriber: StatefulControlSubscriber? = null 73 74 /* 75 * Will track any active load subscriber. Only one can be active at any time. 76 */ 77 private var loadSubscriber: LoadSubscriber? = null 78 79 private val actionCallbackService = object : IControlsActionCallback.Stub() { acceptnull80 override fun accept( 81 token: IBinder, 82 controlId: String, 83 @ControlAction.ResponseResult response: Int 84 ) { 85 backgroundExecutor.execute(OnActionResponseRunnable(token, controlId, response)) 86 } 87 } 88 89 @VisibleForTesting createProviderManagernull90 internal open fun createProviderManager(component: ComponentName): 91 ControlsProviderLifecycleManager { 92 return ControlsProviderLifecycleManager( 93 context, 94 backgroundExecutor, 95 actionCallbackService, 96 currentUser, 97 component, 98 packageUpdateMonitorFactory 99 ) 100 } 101 retrieveLifecycleManagernull102 private fun retrieveLifecycleManager(component: ComponentName): 103 ControlsProviderLifecycleManager { 104 if (currentProvider != null && currentProvider?.componentName != component) { 105 unbind() 106 } 107 108 val provider = currentProvider ?: createProviderManager(component) 109 currentProvider = provider 110 111 return provider 112 } 113 bindAndLoadnull114 override fun bindAndLoad( 115 component: ComponentName, 116 callback: ControlsBindingController.LoadCallback 117 ): Runnable { 118 loadSubscriber?.loadCancel() 119 120 val ls = LoadSubscriber(callback, MAX_CONTROLS_REQUEST) 121 loadSubscriber = ls 122 123 retrieveLifecycleManager(component).maybeBindAndLoad(ls) 124 return ls.loadCancel() 125 } 126 bindAndLoadSuggestednull127 override fun bindAndLoadSuggested( 128 component: ComponentName, 129 callback: ControlsBindingController.LoadCallback 130 ) { 131 loadSubscriber?.loadCancel() 132 val ls = LoadSubscriber(callback, SUGGESTED_CONTROLS_REQUEST) 133 loadSubscriber = ls 134 135 retrieveLifecycleManager(component).maybeBindAndLoadSuggested(ls) 136 } 137 subscribenull138 override fun subscribe(structureInfo: StructureInfo) { 139 // make sure this has happened. only allow one active subscription 140 unsubscribe() 141 142 val provider = retrieveLifecycleManager(structureInfo.componentName) 143 val scs = StatefulControlSubscriber( 144 lazyController.get(), 145 provider, 146 backgroundExecutor, 147 MAX_CONTROLS_REQUEST 148 ) 149 statefulControlSubscriber = scs 150 provider.maybeBindAndSubscribe(structureInfo.controls.map { it.controlId }, scs) 151 } 152 unsubscribenull153 override fun unsubscribe() { 154 statefulControlSubscriber?.cancel() 155 statefulControlSubscriber = null 156 } 157 actionnull158 override fun action( 159 componentName: ComponentName, 160 controlInfo: ControlInfo, 161 action: ControlAction 162 ) { 163 if (statefulControlSubscriber == null) { 164 Log.w(TAG, "No actions can occur outside of an active subscription. Ignoring.") 165 } else { 166 retrieveLifecycleManager(componentName) 167 .maybeBindAndSendAction(controlInfo.controlId, action) 168 } 169 } 170 bindServicenull171 override fun bindService(component: ComponentName) { 172 retrieveLifecycleManager(component).bindService() 173 } 174 bindServiceForPanelnull175 override fun bindServiceForPanel(component: ComponentName) { 176 retrieveLifecycleManager(component).bindServiceForPanel() 177 } 178 changeUsernull179 override fun changeUser(newUser: UserHandle) { 180 if (newUser == currentUser) return 181 182 unbind() 183 currentUser = newUser 184 } 185 unbindnull186 private fun unbind() { 187 unsubscribe() 188 189 loadSubscriber?.loadCancel() 190 loadSubscriber = null 191 192 currentProvider?.unbindService() 193 currentProvider = null 194 } 195 onComponentRemovednull196 override fun onComponentRemoved(componentName: ComponentName) { 197 backgroundExecutor.execute { 198 currentProvider?.let { 199 if (it.componentName == componentName) { 200 unbind() 201 } 202 } 203 } 204 } 205 toStringnull206 override fun toString(): String { 207 return StringBuilder(" ControlsBindingController:\n").apply { 208 append(" currentUser=$currentUser\n") 209 append(" StatefulControlSubscriber=$statefulControlSubscriber") 210 append(" Providers=$currentProvider\n") 211 }.toString() 212 } 213 214 private abstract inner class CallbackRunnable(val token: IBinder) : Runnable { 215 protected val provider: ControlsProviderLifecycleManager? = currentProvider 216 runnull217 override fun run() { 218 if (provider == null) { 219 Log.e(TAG, "No current provider set") 220 return 221 } 222 if (provider.user != currentUser) { 223 Log.e(TAG, "User ${provider.user} is not current user") 224 return 225 } 226 if (token != provider.token) { 227 Log.e(TAG, "Provider for token:$token does not exist anymore") 228 return 229 } 230 231 doRun() 232 } 233 doRunnull234 abstract fun doRun() 235 } 236 237 private inner class OnLoadRunnable( 238 token: IBinder, 239 val list: List<Control>, 240 val callback: ControlsBindingController.LoadCallback 241 ) : CallbackRunnable(token) { 242 override fun doRun() { 243 Log.d(TAG, "LoadSubscription: Complete and loading controls") 244 callback.accept(list) 245 } 246 } 247 248 private inner class OnCancelAndLoadRunnable( 249 token: IBinder, 250 val list: List<Control>, 251 val subscription: IControlsSubscription, 252 val callback: ControlsBindingController.LoadCallback 253 ) : CallbackRunnable(token) { doRunnull254 override fun doRun() { 255 Log.d(TAG, "LoadSubscription: Canceling and loading controls") 256 provider?.cancelSubscription(subscription) 257 callback.accept(list) 258 } 259 } 260 261 private inner class OnSubscribeRunnable( 262 token: IBinder, 263 val subscription: IControlsSubscription, 264 val requestLimit: Long 265 ) : CallbackRunnable(token) { doRunnull266 override fun doRun() { 267 Log.d(TAG, "LoadSubscription: Starting subscription") 268 provider?.startSubscription(subscription, requestLimit) 269 } 270 } 271 272 private inner class OnActionResponseRunnable( 273 token: IBinder, 274 val controlId: String, 275 @ControlAction.ResponseResult val response: Int 276 ) : CallbackRunnable(token) { doRunnull277 override fun doRun() { 278 provider?.let { 279 lazyController.get().onActionResponse(it.componentName, controlId, response) 280 } 281 } 282 } 283 284 private inner class OnLoadErrorRunnable( 285 token: IBinder, 286 val error: String, 287 val callback: ControlsBindingController.LoadCallback 288 ) : CallbackRunnable(token) { doRunnull289 override fun doRun() { 290 callback.error(error) 291 provider?.let { 292 Log.e(TAG, "onError receive from '${it.componentName}': $error") 293 } 294 } 295 } 296 297 private inner class LoadSubscriber( 298 var callback: ControlsBindingController.LoadCallback, 299 val requestLimit: Long 300 ) : IControlsSubscriber.Stub() { 301 val loadedControls = ArrayList<Control>() 302 private var isTerminated = AtomicBoolean(false) 303 private var _loadCancelInternal: (() -> Unit)? = null 304 private lateinit var subscription: IControlsSubscription 305 306 /** 307 * Potentially cancel a subscriber. The subscriber may also have terminated, in which case 308 * the request is ignored. 309 */ <lambda>null310 fun loadCancel() = Runnable { 311 _loadCancelInternal?.let { 312 Log.d(TAG, "Canceling loadSubscribtion") 313 it.invoke() 314 } 315 callback.error("Load cancelled") 316 } 317 onSubscribenull318 override fun onSubscribe(token: IBinder, subs: IControlsSubscription) { 319 subscription = subs 320 _loadCancelInternal = { currentProvider?.cancelSubscription(subscription) } 321 backgroundExecutor.execute(OnSubscribeRunnable(token, subs, requestLimit)) 322 } 323 onNextnull324 override fun onNext(token: IBinder, c: Control) { 325 backgroundExecutor.execute { 326 if (isTerminated.get()) return@execute 327 328 loadedControls.add(c) 329 330 // Once we have reached our requestLimit, send a request to cancel, and immediately 331 // load the results. Calls to onError() and onComplete() are not required after 332 // cancel. 333 if (loadedControls.size >= requestLimit) { 334 maybeTerminateAndRun( 335 OnCancelAndLoadRunnable(token, loadedControls, subscription, callback) 336 ) 337 } 338 } 339 } 340 onErrornull341 override fun onError(token: IBinder, s: String) { 342 maybeTerminateAndRun(OnLoadErrorRunnable(token, s, callback)) 343 } 344 onCompletenull345 override fun onComplete(token: IBinder) { 346 maybeTerminateAndRun(OnLoadRunnable(token, loadedControls, callback)) 347 } 348 maybeTerminateAndRunnull349 private fun maybeTerminateAndRun(postTerminateFn: Runnable) { 350 if (isTerminated.get()) return 351 352 _loadCancelInternal = {} 353 354 // Reassign the callback to clear references to other areas of code. Binders such as 355 // this may not be GC'd right away, so do not hold onto these references. 356 callback = emptyCallback 357 currentProvider?.cancelLoadTimeout() 358 359 backgroundExecutor.execute { 360 isTerminated.compareAndSet(false, true) 361 postTerminateFn.run() 362 } 363 } 364 } 365 } 366 367 private data class Key(val component: ComponentName, val user: UserHandle) 368