1 /* 2 * Copyright (C) 2022 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.media.nearby 18 19 import android.media.INearbyMediaDevicesProvider 20 import android.media.INearbyMediaDevicesUpdateCallback 21 import android.os.IBinder 22 import com.android.systemui.CoreStartable 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.statusbar.CommandQueue 25 import dagger.Binds 26 import dagger.Module 27 import dagger.multibindings.ClassKey 28 import dagger.multibindings.IntoMap 29 import javax.inject.Inject 30 31 /** 32 * A service that acts as a bridge between (1) external clients that have data on nearby devices 33 * that are able to play media and (2) internal clients (like media Output Switcher) that need data 34 * on these nearby devices. 35 */ 36 @SysUISingleton 37 class NearbyMediaDevicesManager @Inject constructor( 38 private val commandQueue: CommandQueue, 39 private val logger: NearbyMediaDevicesLogger 40 ) : CoreStartable { 41 private var providers: MutableList<INearbyMediaDevicesProvider> = mutableListOf() 42 private var activeCallbacks: MutableList<INearbyMediaDevicesUpdateCallback> = mutableListOf() 43 44 private val commandQueueCallbacks = object : CommandQueue.Callbacks { registerNearbyMediaDevicesProvidernull45 override fun registerNearbyMediaDevicesProvider(newProvider: INearbyMediaDevicesProvider) { 46 if (providers.contains(newProvider)) { 47 return 48 } 49 activeCallbacks.forEach { 50 newProvider.registerNearbyDevicesCallback(it) 51 } 52 providers.add(newProvider) 53 logger.logProviderRegistered(providers.size) 54 newProvider.asBinder().linkToDeath(deathRecipient, /* flags= */ 0) 55 } 56 unregisterNearbyMediaDevicesProvidernull57 override fun unregisterNearbyMediaDevicesProvider( 58 newProvider: INearbyMediaDevicesProvider 59 ) { 60 val isRemoved = providers.remove(newProvider) 61 if (isRemoved) { 62 logger.logProviderUnregistered(providers.size) 63 } 64 } 65 } 66 67 private val deathRecipient = object : IBinder.DeathRecipient { binderDiednull68 override fun binderDied() { 69 // Should not be used as binderDied(IBinder who) is overridden. 70 } 71 binderDiednull72 override fun binderDied(who: IBinder) { 73 binderDiedInternal(who) 74 } 75 } 76 startnull77 override fun start() { 78 commandQueue.addCallback(commandQueueCallbacks) 79 } 80 81 /** 82 * Registers [callback] to be notified each time a device's range changes or when a new device 83 * comes within range. 84 * 85 * If a new provider is added, previously-registered callbacks will be registered with the 86 * new provider. 87 */ registerNearbyDevicesCallbacknull88 fun registerNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) { 89 providers.forEach { 90 it.registerNearbyDevicesCallback(callback) 91 } 92 activeCallbacks.add(callback) 93 } 94 95 /** 96 * Un-registers [callback]. See [registerNearbyDevicesCallback]. 97 */ unregisterNearbyDevicesCallbacknull98 fun unregisterNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) { 99 activeCallbacks.remove(callback) 100 providers.forEach { 101 it.unregisterNearbyDevicesCallback(callback) 102 } 103 } 104 binderDiedInternalnull105 private fun binderDiedInternal(who: IBinder) { 106 synchronized(providers) { 107 for (i in providers.size - 1 downTo 0) { 108 if (providers[i].asBinder() == who) { 109 providers.removeAt(i) 110 logger.logProviderBinderDied(providers.size) 111 break 112 } 113 } 114 } 115 } 116 117 @Module 118 interface StartableModule { 119 @Binds 120 @IntoMap 121 @ClassKey(NearbyMediaDevicesManager::class) bindsNearbyMediaDevicesManagernull122 fun bindsNearbyMediaDevicesManager(impl: NearbyMediaDevicesManager): CoreStartable 123 } 124 } 125