Merge changes I3d1952ae,I42f5d4b1 into tm-dev

* changes:
  [Media] Add logging for NearbyMediaDevicesManager.
  [Media] Add logging to MediaMuteAwaitConnectionManager.
This commit is contained in:
TreeHugger Robot
2022-04-19 03:26:54 +00:00
committed by Android (Google) Code Review
10 changed files with 353 additions and 14 deletions

View File

@@ -176,6 +176,29 @@ public class LogModule {
return factory.create("MediaTttReceiver", 20); return factory.create("MediaTttReceiver", 20);
} }
/**
* Provides a logging buffer for logs related to the media mute-await connections. See
* {@link com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager}.
*/
@Provides
@SysUISingleton
@MediaMuteAwaitLog
public static LogBuffer provideMediaMuteAwaitLogBuffer(LogBufferFactory factory) {
return factory.create("MediaMuteAwaitLog", 20);
}
/**
* Provides a logging buffer for logs related to the media mute-await connections. See
* {@link com.android.systemui.media.nearby.NearbyMediaDevicesManager}.
*/
@Provides
@SysUISingleton
@NearbyMediaDevicesLog
public static LogBuffer provideNearbyMediaDevicesLogBuffer(LogBufferFactory factory) {
return factory.create("NearbyMediaDevicesLog", 20);
}
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */ /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
@Provides @Provides
@SysUISingleton @SysUISingleton

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.log.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.systemui.log.LogBuffer;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
* A {@link LogBuffer} for
* {@link com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager}.
*/
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface MediaMuteAwaitLog {
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.log.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.systemui.log.LogBuffer;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/** A {@link LogBuffer} for {@link com.android.systemui.media.nearby.NearbyMediaDevicesManager}. */
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface NearbyMediaDevicesLog {
}

View File

@@ -33,14 +33,13 @@ import java.util.concurrent.Executor
* will be notified. * will be notified.
* *
* See [AudioManager.muteAwaitConnection] and b/206614671 for more details. * See [AudioManager.muteAwaitConnection] and b/206614671 for more details.
*
* TODO(b/206614671): Add logging.
*/ */
class MediaMuteAwaitConnectionManager constructor( class MediaMuteAwaitConnectionManager constructor(
@Main private val mainExecutor: Executor, @Main private val mainExecutor: Executor,
private val localMediaManager: LocalMediaManager, private val localMediaManager: LocalMediaManager,
private val context: Context, private val context: Context,
private val deviceIconUtil: DeviceIconUtil private val deviceIconUtil: DeviceIconUtil,
private val logger: MediaMuteAwaitLogger
) { ) {
var currentMutedDevice: AudioDeviceAttributes? = null var currentMutedDevice: AudioDeviceAttributes? = null
@@ -48,7 +47,8 @@ class MediaMuteAwaitConnectionManager constructor(
val muteAwaitConnectionChangeListener = object : AudioManager.MuteAwaitConnectionCallback() { val muteAwaitConnectionChangeListener = object : AudioManager.MuteAwaitConnectionCallback() {
override fun onMutedUntilConnection(device: AudioDeviceAttributes, mutedUsages: IntArray) { override fun onMutedUntilConnection(device: AudioDeviceAttributes, mutedUsages: IntArray) {
if (USAGE_MEDIA in mutedUsages) { logger.logMutedDeviceAdded(device.address, device.name, mutedUsages.hasMedia())
if (mutedUsages.hasMedia()) {
// There should only be one device that's mutedUntilConnection at a time, so we can // There should only be one device that's mutedUntilConnection at a time, so we can
// safely override any previous value. // safely override any previous value.
currentMutedDevice = device currentMutedDevice = device
@@ -63,7 +63,11 @@ class MediaMuteAwaitConnectionManager constructor(
device: AudioDeviceAttributes, device: AudioDeviceAttributes,
mutedUsages: IntArray mutedUsages: IntArray
) { ) {
if (currentMutedDevice == device && USAGE_MEDIA in mutedUsages) { val isMostRecentDevice = currentMutedDevice == device
logger.logMutedDeviceRemoved(
device.address, device.name, mutedUsages.hasMedia(), isMostRecentDevice
)
if (isMostRecentDevice && mutedUsages.hasMedia()) {
currentMutedDevice = null currentMutedDevice = null
localMediaManager.dispatchAboutToConnectDeviceRemoved() localMediaManager.dispatchAboutToConnectDeviceRemoved()
} }
@@ -92,4 +96,6 @@ class MediaMuteAwaitConnectionManager constructor(
private fun AudioDeviceAttributes.getIcon(): Drawable { private fun AudioDeviceAttributes.getIcon(): Drawable {
return deviceIconUtil.getIconFromAudioDeviceType(this.type, context) return deviceIconUtil.getIconFromAudioDeviceType(this.type, context)
} }
private fun IntArray.hasMedia() = USAGE_MEDIA in this
} }

View File

@@ -30,6 +30,7 @@ import javax.inject.Inject
class MediaMuteAwaitConnectionManagerFactory @Inject constructor( class MediaMuteAwaitConnectionManagerFactory @Inject constructor(
private val mediaFlags: MediaFlags, private val mediaFlags: MediaFlags,
private val context: Context, private val context: Context,
private val logger: MediaMuteAwaitLogger,
@Main private val mainExecutor: Executor @Main private val mainExecutor: Executor
) { ) {
private val deviceIconUtil = DeviceIconUtil() private val deviceIconUtil = DeviceIconUtil()
@@ -40,7 +41,7 @@ class MediaMuteAwaitConnectionManagerFactory @Inject constructor(
return null return null
} }
return MediaMuteAwaitConnectionManager( return MediaMuteAwaitConnectionManager(
mainExecutor, localMediaManager, context, deviceIconUtil mainExecutor, localMediaManager, context, deviceIconUtil, logger
) )
} }
} }

View File

@@ -0,0 +1,51 @@
package com.android.systemui.media.muteawait
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.MediaMuteAwaitLog
import javax.inject.Inject
/** Log messages for [MediaMuteAwaitConnectionManager]. */
@SysUISingleton
class MediaMuteAwaitLogger @Inject constructor(
@MediaMuteAwaitLog private val buffer: LogBuffer
) {
/** Logs that a muted device has been newly added. */
fun logMutedDeviceAdded(deviceAddress: String, deviceName: String, hasMediaUsage: Boolean) =
buffer.log(
TAG,
LogLevel.DEBUG,
{
str1 = deviceAddress
str2 = deviceName
bool1 = hasMediaUsage
},
{
"Muted device added: address=$str1 name=$str2 hasMediaUsage=$bool1"
}
)
/** Logs that a muted device has been removed. */
fun logMutedDeviceRemoved(
deviceAddress: String,
deviceName: String,
hasMediaUsage: Boolean,
isMostRecentDevice: Boolean
) = buffer.log(
TAG,
LogLevel.DEBUG,
{
str1 = deviceAddress
str2 = deviceName
bool1 = hasMediaUsage
bool2 = isMostRecentDevice
},
{
"Muted device removed: " +
"address=$str1 name=$str2 hasMediaUsage=$bool1 isMostRecentDevice=$bool2"
}
)
}
private const val TAG = "MediaMuteAwait"

View File

@@ -0,0 +1,51 @@
package com.android.systemui.media.nearby
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.NearbyMediaDevicesLog
import javax.inject.Inject
/** Log messages for [NearbyMediaDevicesManager]. */
@SysUISingleton
class NearbyMediaDevicesLogger @Inject constructor(
@NearbyMediaDevicesLog private val buffer: LogBuffer
) {
/**
* Log that a new provider was registered.
*
* @param numProviders the total number of providers that are currently registered.
*/
fun logProviderRegistered(numProviders: Int) = buffer.log(
TAG,
LogLevel.DEBUG,
{ int1 = numProviders },
{ "Provider registered; total providers = $int1" }
)
/**
* Log that a new provider was unregistered.
*
* @param numProviders the total number of providers that are currently registered.
*/
fun logProviderUnregistered(numProviders: Int) = buffer.log(
TAG,
LogLevel.DEBUG,
{ int1 = numProviders },
{ "Provider unregistered; total providers = $int1" }
)
/**
* Log that a provider's binder has died.
*
* @param numProviders the total number of providers that are currently registered.
*/
fun logProviderBinderDied(numProviders: Int) = buffer.log(
TAG,
LogLevel.DEBUG,
{ int1 = numProviders },
{ "Provider binder died; total providers = $int1" }
)
}
private const val TAG = "NearbyMediaDevices"

View File

@@ -27,12 +27,11 @@ import javax.inject.Inject
* A service that acts as a bridge between (1) external clients that have data on nearby devices * A service that acts as a bridge between (1) external clients that have data on nearby devices
* that are able to play media and (2) internal clients (like media Output Switcher) that need data * that are able to play media and (2) internal clients (like media Output Switcher) that need data
* on these nearby devices. * on these nearby devices.
*
* TODO(b/216313420): Add logging to this class.
*/ */
@SysUISingleton @SysUISingleton
class NearbyMediaDevicesManager @Inject constructor( class NearbyMediaDevicesManager @Inject constructor(
commandQueue: CommandQueue commandQueue: CommandQueue,
private val logger: NearbyMediaDevicesLogger
) { ) {
private var providers: MutableList<INearbyMediaDevicesProvider> = mutableListOf() private var providers: MutableList<INearbyMediaDevicesProvider> = mutableListOf()
private var activeCallbacks: MutableList<INearbyMediaDevicesUpdateCallback> = mutableListOf() private var activeCallbacks: MutableList<INearbyMediaDevicesUpdateCallback> = mutableListOf()
@@ -46,13 +45,17 @@ class NearbyMediaDevicesManager @Inject constructor(
newProvider.registerNearbyDevicesCallback(it) newProvider.registerNearbyDevicesCallback(it)
} }
providers.add(newProvider) providers.add(newProvider)
logger.logProviderRegistered(providers.size)
newProvider.asBinder().linkToDeath(deathRecipient, /* flags= */ 0) newProvider.asBinder().linkToDeath(deathRecipient, /* flags= */ 0)
} }
override fun unregisterNearbyMediaDevicesProvider( override fun unregisterNearbyMediaDevicesProvider(
newProvider: INearbyMediaDevicesProvider newProvider: INearbyMediaDevicesProvider
) { ) {
providers.remove(newProvider) val isRemoved = providers.remove(newProvider)
if (isRemoved) {
logger.logProviderUnregistered(providers.size)
}
} }
} }
@@ -99,6 +102,7 @@ class NearbyMediaDevicesManager @Inject constructor(
for (i in providers.size - 1 downTo 0) { for (i in providers.size - 1 downTo 0) {
if (providers[i].asBinder() == who) { if (providers[i].asBinder() == who) {
providers.removeAt(i) providers.removeAt(i)
logger.logProviderBinderDied(providers.size)
break break
} }
} }

View File

@@ -53,6 +53,8 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
private lateinit var deviceIconUtil: DeviceIconUtil private lateinit var deviceIconUtil: DeviceIconUtil
@Mock @Mock
private lateinit var localMediaManager: LocalMediaManager private lateinit var localMediaManager: LocalMediaManager
@Mock
private lateinit var logger: MediaMuteAwaitLogger
private lateinit var icon: Drawable private lateinit var icon: Drawable
@Before @Before
@@ -66,7 +68,8 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
FakeExecutor(FakeSystemClock()), FakeExecutor(FakeSystemClock()),
localMediaManager, localMediaManager,
context, context,
deviceIconUtil deviceIconUtil,
logger
) )
} }
@@ -186,6 +189,39 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
verify(localMediaManager).dispatchAboutToConnectDeviceRemoved() verify(localMediaManager).dispatchAboutToConnectDeviceRemoved()
} }
@Test
fun onMutedUntilConnection_isLogged() {
muteAwaitConnectionManager.startListening()
getMuteAwaitListener().onMutedUntilConnection(DEVICE, intArrayOf(USAGE_MEDIA))
verify(logger).logMutedDeviceAdded(DEVICE_ADDRESS, DEVICE_NAME, hasMediaUsage = true)
}
@Test
fun onUnmutedEvent_notMostRecentDevice_isLogged() {
muteAwaitConnectionManager.startListening()
getMuteAwaitListener().onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_MEDIA))
verify(logger).logMutedDeviceRemoved(
DEVICE_ADDRESS, DEVICE_NAME, hasMediaUsage = true, isMostRecentDevice = false
)
}
@Test
fun onUnmutedEvent_isMostRecentDevice_isLogged() {
muteAwaitConnectionManager.startListening()
val muteAwaitListener = getMuteAwaitListener()
muteAwaitListener.onMutedUntilConnection(DEVICE, intArrayOf(USAGE_MEDIA))
muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_MEDIA))
verify(logger).logMutedDeviceRemoved(
DEVICE_ADDRESS, DEVICE_NAME, hasMediaUsage = true, isMostRecentDevice = true
)
}
private fun getMuteAwaitListener(): AudioManager.MuteAwaitConnectionCallback { private fun getMuteAwaitListener(): AudioManager.MuteAwaitConnectionCallback {
val listenerCaptor = ArgumentCaptor.forClass( val listenerCaptor = ArgumentCaptor.forClass(
AudioManager.MuteAwaitConnectionCallback::class.java AudioManager.MuteAwaitConnectionCallback::class.java

View File

@@ -5,13 +5,18 @@ import android.media.INearbyMediaDevicesUpdateCallback
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import android.media.NearbyDevice import android.media.NearbyDevice
import android.os.IBinder
import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.CommandQueue
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.mockito.ArgumentCaptor import org.mockito.ArgumentCaptor
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito import org.mockito.Mockito.anyInt
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
@SmallTest @SmallTest
@@ -19,16 +24,18 @@ class NearbyMediaDevicesManagerTest : SysuiTestCase() {
private lateinit var manager: NearbyMediaDevicesManager private lateinit var manager: NearbyMediaDevicesManager
@Mock @Mock
private lateinit var logger: NearbyMediaDevicesLogger
@Mock
private lateinit var commandQueue: CommandQueue private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallbacks: CommandQueue.Callbacks private lateinit var commandQueueCallbacks: CommandQueue.Callbacks
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
manager = NearbyMediaDevicesManager(commandQueue) manager = NearbyMediaDevicesManager(commandQueue, logger)
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java) val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
Mockito.verify(commandQueue).addCallback(callbackCaptor.capture()) verify(commandQueue).addCallback(callbackCaptor.capture())
commandQueueCallbacks = callbackCaptor.value!! commandQueueCallbacks = callbackCaptor.value!!
} }
@@ -128,9 +135,92 @@ class NearbyMediaDevicesManagerTest : SysuiTestCase() {
assertThat(provider2.lastRegisteredCallback).isEqualTo(callback) assertThat(provider2.lastRegisteredCallback).isEqualTo(callback)
} }
@Test
fun providerUnregistered_doesNotReceiveNewCallback() {
val provider = TestProvider()
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider)
commandQueueCallbacks.unregisterNearbyMediaDevicesProvider(provider)
val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
override fun onDevicesUpdated(nearbyDevices: List<NearbyDevice>) {}
}
manager.registerNearbyDevicesCallback(callback)
assertThat(provider.lastRegisteredCallback).isEqualTo(null)
}
@Test
fun providerRegistered_isLogged() {
commandQueueCallbacks.registerNearbyMediaDevicesProvider(TestProvider())
verify(logger).logProviderRegistered(numProviders = 1)
}
@Test
fun providerRegisteredTwice_onlyLoggedOnce() {
val provider = TestProvider()
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider)
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider)
verify(logger, times(1)).logProviderRegistered(numProviders = 1)
}
@Test
fun multipleProvidersRegistered_isLogged() {
commandQueueCallbacks.registerNearbyMediaDevicesProvider(TestProvider())
commandQueueCallbacks.registerNearbyMediaDevicesProvider(TestProvider())
reset(logger)
commandQueueCallbacks.registerNearbyMediaDevicesProvider(TestProvider())
verify(logger).logProviderRegistered(numProviders = 3)
}
@Test
fun providerUnregistered_isLogged() {
val provider = TestProvider()
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider)
commandQueueCallbacks.unregisterNearbyMediaDevicesProvider(provider)
verify(logger).logProviderUnregistered(numProviders = 0)
}
@Test
fun multipleProvidersRegisteredThenUnregistered_isLogged() {
val provider = TestProvider()
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider)
commandQueueCallbacks.registerNearbyMediaDevicesProvider(TestProvider())
commandQueueCallbacks.registerNearbyMediaDevicesProvider(TestProvider())
commandQueueCallbacks.unregisterNearbyMediaDevicesProvider(provider)
verify(logger).logProviderUnregistered(numProviders = 2)
}
@Test
fun providerUnregisteredButNeverRegistered_notLogged() {
commandQueueCallbacks.unregisterNearbyMediaDevicesProvider(TestProvider())
verify(logger, never()).logProviderRegistered(anyInt())
}
@Test
fun providerBinderDied_isLogged() {
val provider = TestProvider()
commandQueueCallbacks.registerNearbyMediaDevicesProvider(provider)
provider.deathRecipient!!.binderDied(provider)
verify(logger).logProviderBinderDied(numProviders = 0)
}
private class TestProvider : INearbyMediaDevicesProvider.Stub() { private class TestProvider : INearbyMediaDevicesProvider.Stub() {
var lastRegisteredCallback: INearbyMediaDevicesUpdateCallback? = null var lastRegisteredCallback: INearbyMediaDevicesUpdateCallback? = null
var lastUnregisteredCallback: INearbyMediaDevicesUpdateCallback? = null var lastUnregisteredCallback: INearbyMediaDevicesUpdateCallback? = null
var deathRecipient: IBinder.DeathRecipient? = null
override fun registerNearbyDevicesCallback( override fun registerNearbyDevicesCallback(
callback: INearbyMediaDevicesUpdateCallback callback: INearbyMediaDevicesUpdateCallback
) { ) {
@@ -142,5 +232,13 @@ class NearbyMediaDevicesManagerTest : SysuiTestCase() {
) { ) {
lastUnregisteredCallback = callback lastUnregisteredCallback = callback
} }
override fun asBinder(): IBinder {
return this
}
override fun linkToDeath(recipient: IBinder.DeathRecipient, flags: Int) {
deathRecipient = recipient
}
} }
} }