Merge "[Output Switcher] Implement nearby device callbacks."
This commit is contained in:
committed by
Android (Google) Code Review
commit
8333b4f42f
@@ -879,6 +879,12 @@
|
||||
android:name=".media.taptotransfer.sender.MediaTttSenderService"
|
||||
/>
|
||||
|
||||
<!-- Service for external clients to notify us of nearby media devices -->
|
||||
<!-- TODO(b/216313420): Export and guard with a permission. -->
|
||||
<service
|
||||
android:name=".media.nearby.NearbyMediaDevicesService"
|
||||
/>
|
||||
|
||||
<receiver
|
||||
android:name=".tuner.TunerService$ClearReceiver"
|
||||
android:exported="false">
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.shared.media;
|
||||
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback;
|
||||
import com.android.systemui.shared.media.NearbyDevice;
|
||||
|
||||
/**
|
||||
* An interface that provides information about nearby devices that are able to play media.
|
||||
*
|
||||
* External clients will implement this interface and System UI will invoke it if it's passed to
|
||||
* SystemUI via {@link INearbyMediaDevicesService.registerProvider}.
|
||||
*/
|
||||
interface INearbyMediaDevicesProvider {
|
||||
/**
|
||||
* Returns a list of nearby devices that are able to play media.
|
||||
*/
|
||||
List<NearbyDevice> getCurrentNearbyDevices() = 1;
|
||||
|
||||
/**
|
||||
* Registers a callback that will be notified each time the status of a nearby device changes.
|
||||
*/
|
||||
oneway void registerNearbyDevicesCallback(in INearbyMediaDevicesUpdateCallback callback) = 2;
|
||||
|
||||
/**
|
||||
* Unregisters a callback. See {@link registerNearbyDevicesCallback}.
|
||||
*/
|
||||
oneway void unregisterNearbyDevicesCallback(in INearbyMediaDevicesUpdateCallback callback) = 3;
|
||||
}
|
||||
@@ -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.shared.media;
|
||||
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesProvider;
|
||||
|
||||
/**
|
||||
* An interface that can be invoked to notify System UI of nearby media devices.
|
||||
*
|
||||
* External clients wanting to notify System UI about the status of nearby media devices should
|
||||
* implement {@link INearbyMediaDevicesProvider} and then register it with system UI using this
|
||||
* service.
|
||||
*
|
||||
* System UI will implement this interface and external clients will invoke it.
|
||||
*/
|
||||
interface INearbyMediaDevicesService {
|
||||
/** Registers a new provider. */
|
||||
oneway void registerProvider(INearbyMediaDevicesProvider provider) = 1;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.shared.media;
|
||||
|
||||
/**
|
||||
* A callback used to notify implementors of changes in the status of nearby devices that are able
|
||||
* to play media.
|
||||
*
|
||||
* External clients may allow registration of these callbacks and external clients will be
|
||||
* responsible for notifying the callbacks appropriately. System UI is only a mediator between the
|
||||
* external client and these callbacks.
|
||||
*/
|
||||
interface INearbyMediaDevicesUpdateCallback {
|
||||
/** Unknown distance range. */
|
||||
const int RANGE_UNKNOWN = 0;
|
||||
/** Distance is very far away from the peer device. */
|
||||
const int RANGE_FAR = 1;
|
||||
/** Distance is relatively long from the peer device, typically a few meters. */
|
||||
const int RANGE_LONG = 2;
|
||||
/** Distance is close to the peer device, typically with one or two meter. */
|
||||
const int RANGE_CLOSE = 3;
|
||||
/** Distance is very close to the peer device, typically within one meter or less. */
|
||||
const int RANGE_WITHIN_REACH = 4;
|
||||
|
||||
/** Invoked by external clients when media device changes are detected. */
|
||||
oneway void nearbyDeviceUpdate(in String routeId, in int rangeZone) = 1;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.shared.media;
|
||||
|
||||
parcelable NearbyDevice;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.media.nearby
|
||||
package com.android.systemui.shared.media
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
@@ -26,14 +26,15 @@ import android.os.Parcelable
|
||||
* - [routeId] identifying the media route
|
||||
* - [rangeZone] specifying how far away the device with the media route is from this device.
|
||||
*/
|
||||
class NearbyDevice(parcel: Parcel) : Parcelable {
|
||||
var routeId: String? = null
|
||||
class NearbyDevice(
|
||||
val routeId: String?,
|
||||
@RangeZone val rangeZone: Int
|
||||
) : Parcelable {
|
||||
|
||||
init {
|
||||
routeId = parcel.readString() ?: "unknown"
|
||||
private constructor(parcel: Parcel) : this(
|
||||
routeId = parcel.readString() ?: null,
|
||||
rangeZone = parcel.readInt()
|
||||
}
|
||||
)
|
||||
|
||||
override fun describeContents() = 0
|
||||
|
||||
@@ -14,31 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.media.nearby
|
||||
package com.android.systemui.shared.media
|
||||
|
||||
import androidx.annotation.IntDef
|
||||
import kotlin.annotation.AnnotationRetention
|
||||
|
||||
@IntDef(
|
||||
RangeZone.RANGE_UNKNOWN,
|
||||
RangeZone.RANGE_FAR,
|
||||
RangeZone.RANGE_LONG,
|
||||
RangeZone.RANGE_CLOSE,
|
||||
RangeZone.RANGE_WITHIN_REACH
|
||||
INearbyMediaDevicesUpdateCallback.RANGE_UNKNOWN,
|
||||
INearbyMediaDevicesUpdateCallback.RANGE_FAR,
|
||||
INearbyMediaDevicesUpdateCallback.RANGE_LONG,
|
||||
INearbyMediaDevicesUpdateCallback.RANGE_CLOSE,
|
||||
INearbyMediaDevicesUpdateCallback.RANGE_WITHIN_REACH
|
||||
)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
/** The various range zones a device can be in, in relation to the current device. */
|
||||
annotation class RangeZone {
|
||||
companion object {
|
||||
/** Unknown distance range. */
|
||||
const val RANGE_UNKNOWN = 0
|
||||
/** Distance is very far away from the peer device. */
|
||||
const val RANGE_FAR = 1
|
||||
/** Distance is relatively long from the peer device, typically a few meters. */
|
||||
const val RANGE_LONG = 2
|
||||
/** Distance is close to the peer device, typically with one or two meter. */
|
||||
const val RANGE_CLOSE = 3
|
||||
/** Distance is very close to the peer device, typically within one meter or less. */
|
||||
const val RANGE_WITHIN_REACH = 4
|
||||
}
|
||||
}
|
||||
annotation class RangeZone
|
||||
@@ -26,6 +26,7 @@ import com.android.systemui.media.MediaHierarchyManager;
|
||||
import com.android.systemui.media.MediaHost;
|
||||
import com.android.systemui.media.MediaHostStatesManager;
|
||||
import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
|
||||
import com.android.systemui.media.nearby.NearbyMediaDevicesService;
|
||||
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
|
||||
import com.android.systemui.media.taptotransfer.MediaTttFlags;
|
||||
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
|
||||
@@ -142,4 +143,10 @@ public interface MediaModule {
|
||||
@IntoMap
|
||||
@ClassKey(MediaTttSenderService.class)
|
||||
Service bindMediaTttSenderService(MediaTttSenderService service);
|
||||
|
||||
/** Inject into NearbyMediaDevicesService. */
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(NearbyMediaDevicesService.class)
|
||||
Service bindMediaNearbyDevicesService(NearbyMediaDevicesService service);
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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.media.nearby
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
|
||||
/**
|
||||
* A manager that returns information about devices that are nearby and can receive media transfers.
|
||||
*/
|
||||
@SysUISingleton
|
||||
class MediaNearbyDevicesManager {
|
||||
|
||||
/** Returns a list containing the current nearby devices. */
|
||||
fun getCurrentNearbyDevices(): List<NearbyDevice> {
|
||||
// TODO(b/216313420): Implement this function.
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers [callback] to be notified each time a device's range changes or when a new device
|
||||
* comes within range.
|
||||
*/
|
||||
fun registerNearbyDevicesCallback(
|
||||
callback: (device: NearbyDevice) -> Unit
|
||||
) {
|
||||
// TODO(b/216313420): Implement this function.
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-registers [callback]. See [registerNearbyDevicesCallback].
|
||||
*/
|
||||
fun unregisterNearbyDevicesCallback(
|
||||
callback: (device: NearbyDevice) -> Unit
|
||||
) {
|
||||
// TODO(b/216313420): Implement this function.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.media.nearby
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesProvider
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesService
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback
|
||||
import com.android.systemui.shared.media.NearbyDevice
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* 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
|
||||
* on these nearby devices.
|
||||
*
|
||||
* TODO(b/216313420): Add logging to this class.
|
||||
*/
|
||||
@SysUISingleton
|
||||
class NearbyMediaDevicesService @Inject constructor() : Service() {
|
||||
|
||||
private var provider: INearbyMediaDevicesProvider? = null
|
||||
|
||||
private val binder: IBinder = object : INearbyMediaDevicesService.Stub() {
|
||||
override fun registerProvider(newProvider: INearbyMediaDevicesProvider) {
|
||||
provider = newProvider
|
||||
newProvider.asBinder().linkToDeath(
|
||||
{
|
||||
// We might've gotten a new provider before the old provider died, so we only
|
||||
// need to clear our provider if the most recent provider died.
|
||||
if (provider == newProvider) {
|
||||
provider = null
|
||||
}
|
||||
},
|
||||
/* flags= */ 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder = binder
|
||||
|
||||
/** Returns a list containing the current nearby devices. */
|
||||
fun getCurrentNearbyDevices(): List<NearbyDevice> {
|
||||
val currentProvider = provider ?: return emptyList()
|
||||
return currentProvider.currentNearbyDevices
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers [callback] to be notified each time a device's range changes or when a new device
|
||||
* comes within range.
|
||||
*/
|
||||
fun registerNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) {
|
||||
val currentProvider = provider ?: return
|
||||
currentProvider.registerNearbyDevicesCallback(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-registers [callback]. See [registerNearbyDevicesCallback].
|
||||
*/
|
||||
fun unregisterNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) {
|
||||
val currentProvider = provider ?: return
|
||||
currentProvider.unregisterNearbyDevicesCallback(callback)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.android.systemui.media.nearby
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesProvider
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesService
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback.RANGE_LONG
|
||||
import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback.RANGE_WITHIN_REACH
|
||||
import com.android.systemui.shared.media.NearbyDevice
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
@SmallTest
|
||||
class NearbyMediaDevicesServiceTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var service: NearbyMediaDevicesService
|
||||
private lateinit var binderInterface: INearbyMediaDevicesService
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
service = NearbyMediaDevicesService()
|
||||
binderInterface = INearbyMediaDevicesService.Stub.asInterface(service.onBind(null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCurrentNearbyDevices_noProviderRegistered_returnsEmptyList() {
|
||||
assertThat(service.getCurrentNearbyDevices()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCurrentNearbyDevices_providerRegistered_returnsProviderInfo() {
|
||||
val nearbyDevice1 = NearbyDevice("routeId1", RANGE_LONG)
|
||||
val nearbyDevice2 = NearbyDevice("routeId2", RANGE_WITHIN_REACH)
|
||||
val provider = object : INearbyMediaDevicesProvider.Stub() {
|
||||
override fun getCurrentNearbyDevices(): List<NearbyDevice> {
|
||||
return listOf(nearbyDevice1, nearbyDevice2)
|
||||
}
|
||||
|
||||
override fun registerNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback?
|
||||
) {}
|
||||
override fun unregisterNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback?
|
||||
) {}
|
||||
}
|
||||
binderInterface.registerProvider(provider)
|
||||
|
||||
val returnedNearbyDevices = service.getCurrentNearbyDevices()
|
||||
|
||||
assertThat(returnedNearbyDevices).isEqualTo(listOf(nearbyDevice1, nearbyDevice2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun registerNearbyDevicesCallback_noProviderRegistered_noCrash() {
|
||||
// No assert, just needs no crash
|
||||
service.registerNearbyDevicesCallback(object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun registerNearbyDevicesCallback_providerRegistered_providerReceivesCallback() {
|
||||
val provider = object : INearbyMediaDevicesProvider.Stub() {
|
||||
var registeredCallback: INearbyMediaDevicesUpdateCallback? = null
|
||||
override fun getCurrentNearbyDevices(): List<NearbyDevice> = listOf()
|
||||
|
||||
override fun registerNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback?
|
||||
) {
|
||||
registeredCallback = callback
|
||||
}
|
||||
|
||||
override fun unregisterNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback?
|
||||
) {}
|
||||
}
|
||||
binderInterface.registerProvider(provider)
|
||||
|
||||
val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
|
||||
}
|
||||
|
||||
service.registerNearbyDevicesCallback(callback)
|
||||
|
||||
assertThat(provider.registeredCallback).isEqualTo(callback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unregisterNearbyDevicesCallback_noProviderRegistered_noCrash() {
|
||||
// No assert, just needs no crash
|
||||
service.unregisterNearbyDevicesCallback(object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unregisterNearbyDevicesCallback_providerRegistered_providerReceivesCallback() {
|
||||
val provider = object : INearbyMediaDevicesProvider.Stub() {
|
||||
var unregisteredCallback: INearbyMediaDevicesUpdateCallback? = null
|
||||
override fun getCurrentNearbyDevices(): List<NearbyDevice> = listOf()
|
||||
|
||||
override fun registerNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback?
|
||||
) {}
|
||||
|
||||
override fun unregisterNearbyDevicesCallback(
|
||||
callback: INearbyMediaDevicesUpdateCallback?
|
||||
) {
|
||||
unregisteredCallback = callback
|
||||
}
|
||||
}
|
||||
binderInterface.registerProvider(provider)
|
||||
|
||||
val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
|
||||
override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
|
||||
}
|
||||
|
||||
service.unregisterNearbyDevicesCallback(callback)
|
||||
|
||||
assertThat(provider.unregisteredCallback).isEqualTo(callback)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user