Loading android/app/src/com/android/bluetooth/vc/VolumeControlInputDescriptor.java +4 −1 Original line number Diff line number Diff line Loading @@ -220,7 +220,10 @@ class VolumeControlInputDescriptor { Descriptor desc = mVolumeInputs[id]; if (gainSetting > desc.mGainSettingsMax || gainSetting < desc.mGainSettingsMin) { throw new IllegalArgumentException("Illegal gainSetting argument: " + gainSetting); throw new IllegalArgumentException( ("gainSetting=" + gainSetting + " is not in correct range") + (" [" + desc.mGainSettingsMin + "-" + desc.mGainSettingsMax + "]")); } if (desc.mGainMode == bluetooth.constants.aics.GainMode.AUTOMATIC Loading android/pandora/mmi2grpc/mmi2grpc/vcp.py +87 −9 Original line number Diff line number Diff line Loading @@ -94,8 +94,8 @@ class VCPProxy(ProfileProxy): def IUT_INITIATE_DISCOVER_CHARACTERISTIC(self, **kwargs): """ Please take action to discover the (Volume Control Point|Volume State|Volume Flags|Offset State|Volume Offset Control Point) characteristic from the Volume (Offset )?Control. Discover the primary service if needed. (Volume (Control Point|State|Flags|Offset Control Point)|Offset State|Audio Input (State|Type|Status|Control Point|Description)|Gain Setting Properties) characteristic from the (Volume( Offset)?|Audio Input) Control. Discover the primary service if needed. Description: Verify that the Implementation Under Test \(IUT\) can send Discover All Characteristics command. """ Loading @@ -108,9 +108,10 @@ class VCPProxy(ProfileProxy): @match_description def IUT_READ_CHARACTERISTIC(self, name: str, handle: str, **kwargs): """ Please send Read Request to read (?P<name>(Volume State|Volume Flags|Offset State)) characteristic with handle Please send Read Request to read (?P<name>(Volume State|Volume Flags|Offset State|Audio Input (State|Status|Type)|Gain Setting Properties)) characteristic with handle = (?P<handle>(0x[0-9A-Fa-f]{4})). """ # After discovery Android reads these values by itself, after profile connection. # Although, for some tests, this is used as validation, for example for tests with invalid # behavior (BI tests). Just send GATT read to sattisfy this conditions, as VCP has no exposed Loading Loading @@ -139,7 +140,7 @@ class VCPProxy(ProfileProxy): def IUT_CONFIG_NOTIFICATION(self, name: str, **kwargs): """ Please write to Client Characteristic Configuration Descriptor of (?P<name>(Volume State|Offset State)) characteristic to enable notification. (?P<name>(Volume State|Offset State|Audio Input State)) characteristic to enable notification.(.*) """ # After discovery Android subscribes by itself, after profile connection Loading @@ -149,12 +150,12 @@ class VCPProxy(ProfileProxy): def IUT_SEND_WRITE_REQUEST(self, description: str, chr_name: str, op_code: str, **kwargs): r""" Please send write request to handle 0x([0-9A-Fa-f]{4}) with following value. (?P<chr_name>(Volume Control Point|Volume Offset Control Point)): Op Code: (?P<op_code>((<WildCard: Exists>)|(\[[0-9] \(0x0[0-9]\)\]\s([\w]*\s){1,3})))(.*) (?P<chr_name>(Volume Control Point|Volume Offset Control Point|Audio Input Control Point)): Op Code: (?P<op_code>((<WildCard: Exists>)|(\[[0-9] \(0x0[0-9]\)\]\s([\w]*\s){1,4})))(.*) """ # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed) sleep(2) sleep(4) if (chr_name == "Volume Control Point"): if "Set Absolute Volume" in op_code: Loading @@ -166,9 +167,23 @@ class VCPProxy(ProfileProxy): # Handles sending *any* OP Code on Volume Control Point self.vcp.SetDeviceVolume(connection=self.connection, volume=42) elif (chr_name == "Volume Offset Control Point"): if ("Set Volume Offset" in op_code or "<WildCard: Exists>" in op_code): if ("Set Volume Offset" in op_code or "<WildCard: Exists>" in op_code): self.vcp.SetVolumeOffset(connection=self.connection, offset=42) elif (chr_name == "Audio Input Control Point"): if "[1 (0x01)] Set Gain Setting" in op_code: self.vcp.SetGainSetting(connection=self.connection, gainSetting=42) elif "[2 (0x02)] Unmute" in op_code: self.vcp.SetMute(connection=self.connection, mute=0x00) elif "[3 (0x03)] Mute" in op_code: self.vcp.SetMute(connection=self.connection, mute=0x01) elif "[4 (0x04)] Set Manual Gain Mode" in op_code: self.vcp.SetGainMode(connection=self.connection, gainMode=0x02) elif "[5 (0x05)] Set Automatic Gain Mode" in op_code: self.vcp.SetGainMode(connection=self.connection, gainMode=0x03) elif "<WildCard: Exists>" in op_code: self.vcp.SetMute(connection=self.connection, mute=0) else: assert False, f'Unhandled op_code in IUT_SEND_WRITE_REQUEST:\n{op_code}' else: return "No" Loading @@ -181,3 +196,66 @@ class VCPProxy(ProfileProxy): otherwise click 'No'. """ return "OK" @assert_description def IUT_WRITE_GAIN_SETTING_MAX(self, **kwargs): """ Please write to Audio Input Control Point with the Set Gain Setting Op Code value of 0x01, the Gain Setting parameters set to a random value greater than 100 and the Change Counter parameter set. """ # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed) sleep(4) self.vcp.SetGainSetting(connection=self.connection, gainSetting=101) return "OK" @assert_description def IUT_WRITE_UNMUTE_OPCODE(self, **kwargs): """ Please write to Audio Input Control Point with the Unmute Op Code. """ # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed) sleep(4) self.vcp.SetMute(connection=self.connection, mute=0x00) return "OK" @assert_description def IUT_WRITE_MUTE_OPCODE(self, **kwargs): """ Please write to Audio Input Control Point with the Mute Op Code. """ # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed) sleep(4) self.vcp.SetMute(connection=self.connection, mute=0x01) return "OK" @assert_description def IUT_WRITE_SET_MANUAL_GAIN_MODE_OPCODE(self, **kwargs): """ Please write to Audio Input Control Point with the Set Manual Op Code. """ # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed) sleep(4) self.vcp.SetGainMode(connection=self.connection, gainMode=0x02) return "OK" @assert_description def IUT_WRITE_SET_AUTOMATIC_GAIN_MODE_OPCODE(self, **kwargs): """ Please write to Audio Input Control Point with the Set Automatic Op Code. """ # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed) sleep(4) self.vcp.SetGainMode(connection=self.connection, gainMode=0x03) return "OK" android/pandora/server/configs/pts_bot_tests_config.json +73 −0 Original line number Diff line number Diff line Loading @@ -686,17 +686,38 @@ "SM/PER/SCJW/BV-03-C", "SM/PER/SCPK/BI-03-C", "SM/PER/SCPK/BV-02-C", "VCP/VC/AICP/BV-01-C", "VCP/VC/AICP/BV-02-C", "VCP/VC/AICP/BV-03-C", "VCP/VC/AICP/BV-04-C", "VCP/VC/AICP/BV-05-C", "VCP/VC/CGGIT/CHA/BV-01-C", "VCP/VC/CGGIT/CHA/BV-02-C", "VCP/VC/CGGIT/CHA/BV-03-C", "VCP/VC/CGGIT/CHA/BV-04-C", "VCP/VC/CGGIT/CHA/BV-06-C", "VCP/VC/CGGIT/CHA/BV-08-C", "VCP/VC/CGGIT/CHA/BV-09-C", "VCP/VC/CGGIT/CHA/BV-10-C", "VCP/VC/CGGIT/CHA/BV-11-C", "VCP/VC/CGGIT/CHA/BV-12-C", "VCP/VC/CGGIT/CHA/BV-13-C", "VCP/VC/CGGIT/SER/BV-01-C", "VCP/VC/CGGIT/SER/BV-02-C", "VCP/VC/CGGIT/SER/BV-03-C", "VCP/VC/SPE/BI-05-C", "VCP/VC/SPE/BI-08-C", "VCP/VC/SPE/BI-09-C", "VCP/VC/SPE/BI-10-C", "VCP/VC/SPE/BI-11-C", "VCP/VC/SPE/BI-12-C", "VCP/VC/SPE/BI-13-C", "VCP/VC/SPE/BI-15-C", "VCP/VC/SPE/BI-16-C", "VCP/VC/SPE/BI-17-C", "VCP/VC/SPE/BI-18-C", "VCP/VC/SPE/BI-19-C", "VCP/VC/SPE/BI-20-C", "VCP/VC/VCCP/BV-05-C", "VCP/VC/VOCP/BV-01-C" ], Loading Loading @@ -1158,6 +1179,7 @@ "SM/PER/SIE/BV-01-C", "SM/PER/SIP/BV-01-C", "VCP/VC/SPE/BI-06-C", "VCP/VC/SPE/BI-14-C", "VCP/VC/VCCP/BV-06-C" ], "ics": { Loading Loading @@ -3056,6 +3078,7 @@ "TSPC_VCP_8_1": true, "TSPC_VCP_10_1": true, "TSPC_VCP_10_2": true, "TSPC_VCP_10_3": true, "TSPC_VCP_11_1": true, "TSPC_VCP_11_2": true, "TSPC_VCP_11_3": true, Loading @@ -3073,6 +3096,26 @@ "TSPC_VCP_14_3": true, "TSPC_VCP_14_4": true, "TSPC_VCP_14_6": true, "TSPC_VCP_15_1": true, "TSPC_VCP_15_2": true, "TSPC_VCP_15_3": true, "TSPC_VCP_15_4": true, "TSPC_VCP_15_5": true, "TSPC_VCP_15_6": true, "TSPC_VCP_16_1": true, "TSPC_VCP_16_2": true, "TSPC_VCP_16_3": true, "TSPC_VCP_16_4": true, "TSPC_VCP_16_5": true, "TSPC_VCP_16_6": true, "TSPC_VCP_16_7": true, "TSPC_VCP_16_8": true, "TSPC_VCP_16_9": true, "TSPC_VCP_16_10": true, "TSPC_VCP_16_11": true, "TSPC_VCP_16_12": true, "TSPC_VCP_16_13": true, "TSPC_VCP_16_14": true, "TSPC_VCP_17_1": true, "TSPC_VCP_17_2": true, "TSPC_VCP_17_3": true, Loading Loading @@ -3188,6 +3231,36 @@ "tests": [ "HAP" ] }, { "flags": [ "leaudio_add_aics_support", "aics_api" ], "tests": [ "VCP/VC/AICP/BV-01-C", "VCP/VC/AICP/BV-02-C", "VCP/VC/AICP/BV-03-C", "VCP/VC/AICP/BV-04-C", "VCP/VC/AICP/BV-05-C", "VCP/VC/CGGIT/CHA/BV-08-C", "VCP/VC/CGGIT/CHA/BV-09-C", "VCP/VC/CGGIT/CHA/BV-10-C", "VCP/VC/CGGIT/CHA/BV-11-C", "VCP/VC/CGGIT/CHA/BV-12-C", "VCP/VC/CGGIT/CHA/BV-13-C", "VCP/VC/CGGIT/SER/BV-03-C", "VCP/VC/SPE/BI-08-C", "VCP/VC/SPE/BI-09-C", "VCP/VC/SPE/BI-10-C", "VCP/VC/SPE/BI-11-C", "VCP/VC/SPE/BI-12-C", "VCP/VC/SPE/BI-14-C", "VCP/VC/SPE/BI-17-C", "VCP/VC/SPE/BI-18-C", "VCP/VC/SPE/BI-19-C", "VCP/VC/SPE/BI-20-C" ] } ] } android/pandora/server/src/Vcp.kt +49 −20 Original line number Diff line number Diff line Loading @@ -17,37 +17,26 @@ package com.android.pandora import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice.TRANSPORT_LE import android.bluetooth.BluetoothVolumeControl import android.bluetooth.BluetoothManager import android.bluetooth.BluetoothProfile import android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED import android.bluetooth.BluetoothVolumeControl import android.content.Context import android.content.Intent import android.content.IntentFilter import android.util.Log import com.google.protobuf.Empty import io.grpc.Status import io.grpc.stub.StreamObserver import java.io.Closeable import java.io.PrintWriter import java.io.StringWriter import java.util.concurrent.Executors import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import pandora.vcp.VCPGrpc.VCPImplBase import pandora.vcp.VcpProto.* import pandora.HostProto.Connection @kotlinx.coroutines.ExperimentalCoroutinesApi class Vcp(val context: Context) : VCPImplBase(), Closeable { Loading Loading @@ -78,7 +67,7 @@ class Vcp(val context: Context) : VCPImplBase(), Closeable { override fun setDeviceVolume( request: SetDeviceVolumeRequest, responseObserver: StreamObserver<Empty> responseObserver: StreamObserver<Empty>, ) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) Loading @@ -93,7 +82,7 @@ class Vcp(val context: Context) : VCPImplBase(), Closeable { override fun setVolumeOffset( request: SetVolumeOffsetRequest, responseObserver: StreamObserver<Empty> responseObserver: StreamObserver<Empty>, ) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) Loading @@ -106,15 +95,13 @@ class Vcp(val context: Context) : VCPImplBase(), Closeable { } } override fun waitConnect( request: WaitConnectRequest, responseObserver: StreamObserver<Empty> ) { override fun waitConnect(request: WaitConnectRequest, responseObserver: StreamObserver<Empty>) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) Log.i(TAG, "waitPeripheral(${device}") if ( bluetoothVolumeControl.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED bluetoothVolumeControl.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED ) { Log.d(TAG, "Manual call to setConnectionPolicy") bluetoothVolumeControl.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED) Loading @@ -129,4 +116,46 @@ class Vcp(val context: Context) : VCPImplBase(), Closeable { Empty.getDefaultInstance() } } override fun setGainSetting( request: SetGainSettingRequest, responseObserver: StreamObserver<Empty>, ) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) Log.i(TAG, "setGainSetting(${device}, ${request.gainSetting})") bluetoothVolumeControl.getAudioInputControlServices(device).forEach { it.setGainSetting(request.gainSetting) } Empty.getDefaultInstance() } } override fun setMute(request: SetMuteRequest, responseObserver: StreamObserver<Empty>) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) Log.i(TAG, "setMute(${device}, ${request.mute})") bluetoothVolumeControl.getAudioInputControlServices(device).forEach { it.setMute(request.mute) } Empty.getDefaultInstance() } } override fun setGainMode(request: SetGainModeRequest, responseObserver: StreamObserver<Empty>) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) Log.i(TAG, "setMute(${device}, ${request.gainMode})") bluetoothVolumeControl.getAudioInputControlServices(device).forEach { it.setGainMode(request.gainMode) } Empty.getDefaultInstance() } } } pandora/interfaces/pandora_experimental/vcp.proto +23 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,14 @@ service VCP { rpc SetVolumeOffset(SetVolumeOffsetRequest) returns (google.protobuf.Empty); // Wait for device to be connected. rpc WaitConnect(WaitConnectRequest) returns (google.protobuf.Empty); // TODO: AICS interfaces could be made a bit more generic by first fetching // what are the available instances and using them like the Connection object // is used. rpc SetGainSetting(SetGainSettingRequest) returns (google.protobuf.Empty); rpc SetMute(SetMuteRequest) returns (google.protobuf.Empty); rpc SetGainMode(SetGainModeRequest) returns (google.protobuf.Empty); } // Request of the `SetDeviceVolume` method Loading @@ -45,3 +53,18 @@ message SetVolumeOffsetRequest{ message WaitConnectRequest { Connection connection = 1; } message SetGainSettingRequest { Connection connection = 1; int32 gainSetting = 2; } message SetMuteRequest { Connection connection = 1; // See Mute.aidl for valid values int32 mute = 2; } message SetGainModeRequest { Connection connection = 1; // See GainMode.aidl for valid values int32 gainMode = 2; } Loading
android/app/src/com/android/bluetooth/vc/VolumeControlInputDescriptor.java +4 −1 Original line number Diff line number Diff line Loading @@ -220,7 +220,10 @@ class VolumeControlInputDescriptor { Descriptor desc = mVolumeInputs[id]; if (gainSetting > desc.mGainSettingsMax || gainSetting < desc.mGainSettingsMin) { throw new IllegalArgumentException("Illegal gainSetting argument: " + gainSetting); throw new IllegalArgumentException( ("gainSetting=" + gainSetting + " is not in correct range") + (" [" + desc.mGainSettingsMin + "-" + desc.mGainSettingsMax + "]")); } if (desc.mGainMode == bluetooth.constants.aics.GainMode.AUTOMATIC Loading
android/pandora/mmi2grpc/mmi2grpc/vcp.py +87 −9 Original line number Diff line number Diff line Loading @@ -94,8 +94,8 @@ class VCPProxy(ProfileProxy): def IUT_INITIATE_DISCOVER_CHARACTERISTIC(self, **kwargs): """ Please take action to discover the (Volume Control Point|Volume State|Volume Flags|Offset State|Volume Offset Control Point) characteristic from the Volume (Offset )?Control. Discover the primary service if needed. (Volume (Control Point|State|Flags|Offset Control Point)|Offset State|Audio Input (State|Type|Status|Control Point|Description)|Gain Setting Properties) characteristic from the (Volume( Offset)?|Audio Input) Control. Discover the primary service if needed. Description: Verify that the Implementation Under Test \(IUT\) can send Discover All Characteristics command. """ Loading @@ -108,9 +108,10 @@ class VCPProxy(ProfileProxy): @match_description def IUT_READ_CHARACTERISTIC(self, name: str, handle: str, **kwargs): """ Please send Read Request to read (?P<name>(Volume State|Volume Flags|Offset State)) characteristic with handle Please send Read Request to read (?P<name>(Volume State|Volume Flags|Offset State|Audio Input (State|Status|Type)|Gain Setting Properties)) characteristic with handle = (?P<handle>(0x[0-9A-Fa-f]{4})). """ # After discovery Android reads these values by itself, after profile connection. # Although, for some tests, this is used as validation, for example for tests with invalid # behavior (BI tests). Just send GATT read to sattisfy this conditions, as VCP has no exposed Loading Loading @@ -139,7 +140,7 @@ class VCPProxy(ProfileProxy): def IUT_CONFIG_NOTIFICATION(self, name: str, **kwargs): """ Please write to Client Characteristic Configuration Descriptor of (?P<name>(Volume State|Offset State)) characteristic to enable notification. (?P<name>(Volume State|Offset State|Audio Input State)) characteristic to enable notification.(.*) """ # After discovery Android subscribes by itself, after profile connection Loading @@ -149,12 +150,12 @@ class VCPProxy(ProfileProxy): def IUT_SEND_WRITE_REQUEST(self, description: str, chr_name: str, op_code: str, **kwargs): r""" Please send write request to handle 0x([0-9A-Fa-f]{4}) with following value. (?P<chr_name>(Volume Control Point|Volume Offset Control Point)): Op Code: (?P<op_code>((<WildCard: Exists>)|(\[[0-9] \(0x0[0-9]\)\]\s([\w]*\s){1,3})))(.*) (?P<chr_name>(Volume Control Point|Volume Offset Control Point|Audio Input Control Point)): Op Code: (?P<op_code>((<WildCard: Exists>)|(\[[0-9] \(0x0[0-9]\)\]\s([\w]*\s){1,4})))(.*) """ # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed) sleep(2) sleep(4) if (chr_name == "Volume Control Point"): if "Set Absolute Volume" in op_code: Loading @@ -166,9 +167,23 @@ class VCPProxy(ProfileProxy): # Handles sending *any* OP Code on Volume Control Point self.vcp.SetDeviceVolume(connection=self.connection, volume=42) elif (chr_name == "Volume Offset Control Point"): if ("Set Volume Offset" in op_code or "<WildCard: Exists>" in op_code): if ("Set Volume Offset" in op_code or "<WildCard: Exists>" in op_code): self.vcp.SetVolumeOffset(connection=self.connection, offset=42) elif (chr_name == "Audio Input Control Point"): if "[1 (0x01)] Set Gain Setting" in op_code: self.vcp.SetGainSetting(connection=self.connection, gainSetting=42) elif "[2 (0x02)] Unmute" in op_code: self.vcp.SetMute(connection=self.connection, mute=0x00) elif "[3 (0x03)] Mute" in op_code: self.vcp.SetMute(connection=self.connection, mute=0x01) elif "[4 (0x04)] Set Manual Gain Mode" in op_code: self.vcp.SetGainMode(connection=self.connection, gainMode=0x02) elif "[5 (0x05)] Set Automatic Gain Mode" in op_code: self.vcp.SetGainMode(connection=self.connection, gainMode=0x03) elif "<WildCard: Exists>" in op_code: self.vcp.SetMute(connection=self.connection, mute=0) else: assert False, f'Unhandled op_code in IUT_SEND_WRITE_REQUEST:\n{op_code}' else: return "No" Loading @@ -181,3 +196,66 @@ class VCPProxy(ProfileProxy): otherwise click 'No'. """ return "OK" @assert_description def IUT_WRITE_GAIN_SETTING_MAX(self, **kwargs): """ Please write to Audio Input Control Point with the Set Gain Setting Op Code value of 0x01, the Gain Setting parameters set to a random value greater than 100 and the Change Counter parameter set. """ # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed) sleep(4) self.vcp.SetGainSetting(connection=self.connection, gainSetting=101) return "OK" @assert_description def IUT_WRITE_UNMUTE_OPCODE(self, **kwargs): """ Please write to Audio Input Control Point with the Unmute Op Code. """ # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed) sleep(4) self.vcp.SetMute(connection=self.connection, mute=0x00) return "OK" @assert_description def IUT_WRITE_MUTE_OPCODE(self, **kwargs): """ Please write to Audio Input Control Point with the Mute Op Code. """ # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed) sleep(4) self.vcp.SetMute(connection=self.connection, mute=0x01) return "OK" @assert_description def IUT_WRITE_SET_MANUAL_GAIN_MODE_OPCODE(self, **kwargs): """ Please write to Audio Input Control Point with the Set Manual Op Code. """ # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed) sleep(4) self.vcp.SetGainMode(connection=self.connection, gainMode=0x02) return "OK" @assert_description def IUT_WRITE_SET_AUTOMATIC_GAIN_MODE_OPCODE(self, **kwargs): """ Please write to Audio Input Control Point with the Set Automatic Op Code. """ # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed) sleep(4) self.vcp.SetGainMode(connection=self.connection, gainMode=0x03) return "OK"
android/pandora/server/configs/pts_bot_tests_config.json +73 −0 Original line number Diff line number Diff line Loading @@ -686,17 +686,38 @@ "SM/PER/SCJW/BV-03-C", "SM/PER/SCPK/BI-03-C", "SM/PER/SCPK/BV-02-C", "VCP/VC/AICP/BV-01-C", "VCP/VC/AICP/BV-02-C", "VCP/VC/AICP/BV-03-C", "VCP/VC/AICP/BV-04-C", "VCP/VC/AICP/BV-05-C", "VCP/VC/CGGIT/CHA/BV-01-C", "VCP/VC/CGGIT/CHA/BV-02-C", "VCP/VC/CGGIT/CHA/BV-03-C", "VCP/VC/CGGIT/CHA/BV-04-C", "VCP/VC/CGGIT/CHA/BV-06-C", "VCP/VC/CGGIT/CHA/BV-08-C", "VCP/VC/CGGIT/CHA/BV-09-C", "VCP/VC/CGGIT/CHA/BV-10-C", "VCP/VC/CGGIT/CHA/BV-11-C", "VCP/VC/CGGIT/CHA/BV-12-C", "VCP/VC/CGGIT/CHA/BV-13-C", "VCP/VC/CGGIT/SER/BV-01-C", "VCP/VC/CGGIT/SER/BV-02-C", "VCP/VC/CGGIT/SER/BV-03-C", "VCP/VC/SPE/BI-05-C", "VCP/VC/SPE/BI-08-C", "VCP/VC/SPE/BI-09-C", "VCP/VC/SPE/BI-10-C", "VCP/VC/SPE/BI-11-C", "VCP/VC/SPE/BI-12-C", "VCP/VC/SPE/BI-13-C", "VCP/VC/SPE/BI-15-C", "VCP/VC/SPE/BI-16-C", "VCP/VC/SPE/BI-17-C", "VCP/VC/SPE/BI-18-C", "VCP/VC/SPE/BI-19-C", "VCP/VC/SPE/BI-20-C", "VCP/VC/VCCP/BV-05-C", "VCP/VC/VOCP/BV-01-C" ], Loading Loading @@ -1158,6 +1179,7 @@ "SM/PER/SIE/BV-01-C", "SM/PER/SIP/BV-01-C", "VCP/VC/SPE/BI-06-C", "VCP/VC/SPE/BI-14-C", "VCP/VC/VCCP/BV-06-C" ], "ics": { Loading Loading @@ -3056,6 +3078,7 @@ "TSPC_VCP_8_1": true, "TSPC_VCP_10_1": true, "TSPC_VCP_10_2": true, "TSPC_VCP_10_3": true, "TSPC_VCP_11_1": true, "TSPC_VCP_11_2": true, "TSPC_VCP_11_3": true, Loading @@ -3073,6 +3096,26 @@ "TSPC_VCP_14_3": true, "TSPC_VCP_14_4": true, "TSPC_VCP_14_6": true, "TSPC_VCP_15_1": true, "TSPC_VCP_15_2": true, "TSPC_VCP_15_3": true, "TSPC_VCP_15_4": true, "TSPC_VCP_15_5": true, "TSPC_VCP_15_6": true, "TSPC_VCP_16_1": true, "TSPC_VCP_16_2": true, "TSPC_VCP_16_3": true, "TSPC_VCP_16_4": true, "TSPC_VCP_16_5": true, "TSPC_VCP_16_6": true, "TSPC_VCP_16_7": true, "TSPC_VCP_16_8": true, "TSPC_VCP_16_9": true, "TSPC_VCP_16_10": true, "TSPC_VCP_16_11": true, "TSPC_VCP_16_12": true, "TSPC_VCP_16_13": true, "TSPC_VCP_16_14": true, "TSPC_VCP_17_1": true, "TSPC_VCP_17_2": true, "TSPC_VCP_17_3": true, Loading Loading @@ -3188,6 +3231,36 @@ "tests": [ "HAP" ] }, { "flags": [ "leaudio_add_aics_support", "aics_api" ], "tests": [ "VCP/VC/AICP/BV-01-C", "VCP/VC/AICP/BV-02-C", "VCP/VC/AICP/BV-03-C", "VCP/VC/AICP/BV-04-C", "VCP/VC/AICP/BV-05-C", "VCP/VC/CGGIT/CHA/BV-08-C", "VCP/VC/CGGIT/CHA/BV-09-C", "VCP/VC/CGGIT/CHA/BV-10-C", "VCP/VC/CGGIT/CHA/BV-11-C", "VCP/VC/CGGIT/CHA/BV-12-C", "VCP/VC/CGGIT/CHA/BV-13-C", "VCP/VC/CGGIT/SER/BV-03-C", "VCP/VC/SPE/BI-08-C", "VCP/VC/SPE/BI-09-C", "VCP/VC/SPE/BI-10-C", "VCP/VC/SPE/BI-11-C", "VCP/VC/SPE/BI-12-C", "VCP/VC/SPE/BI-14-C", "VCP/VC/SPE/BI-17-C", "VCP/VC/SPE/BI-18-C", "VCP/VC/SPE/BI-19-C", "VCP/VC/SPE/BI-20-C" ] } ] }
android/pandora/server/src/Vcp.kt +49 −20 Original line number Diff line number Diff line Loading @@ -17,37 +17,26 @@ package com.android.pandora import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice.TRANSPORT_LE import android.bluetooth.BluetoothVolumeControl import android.bluetooth.BluetoothManager import android.bluetooth.BluetoothProfile import android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED import android.bluetooth.BluetoothVolumeControl import android.content.Context import android.content.Intent import android.content.IntentFilter import android.util.Log import com.google.protobuf.Empty import io.grpc.Status import io.grpc.stub.StreamObserver import java.io.Closeable import java.io.PrintWriter import java.io.StringWriter import java.util.concurrent.Executors import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import pandora.vcp.VCPGrpc.VCPImplBase import pandora.vcp.VcpProto.* import pandora.HostProto.Connection @kotlinx.coroutines.ExperimentalCoroutinesApi class Vcp(val context: Context) : VCPImplBase(), Closeable { Loading Loading @@ -78,7 +67,7 @@ class Vcp(val context: Context) : VCPImplBase(), Closeable { override fun setDeviceVolume( request: SetDeviceVolumeRequest, responseObserver: StreamObserver<Empty> responseObserver: StreamObserver<Empty>, ) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) Loading @@ -93,7 +82,7 @@ class Vcp(val context: Context) : VCPImplBase(), Closeable { override fun setVolumeOffset( request: SetVolumeOffsetRequest, responseObserver: StreamObserver<Empty> responseObserver: StreamObserver<Empty>, ) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) Loading @@ -106,15 +95,13 @@ class Vcp(val context: Context) : VCPImplBase(), Closeable { } } override fun waitConnect( request: WaitConnectRequest, responseObserver: StreamObserver<Empty> ) { override fun waitConnect(request: WaitConnectRequest, responseObserver: StreamObserver<Empty>) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) Log.i(TAG, "waitPeripheral(${device}") if ( bluetoothVolumeControl.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED bluetoothVolumeControl.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED ) { Log.d(TAG, "Manual call to setConnectionPolicy") bluetoothVolumeControl.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED) Loading @@ -129,4 +116,46 @@ class Vcp(val context: Context) : VCPImplBase(), Closeable { Empty.getDefaultInstance() } } override fun setGainSetting( request: SetGainSettingRequest, responseObserver: StreamObserver<Empty>, ) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) Log.i(TAG, "setGainSetting(${device}, ${request.gainSetting})") bluetoothVolumeControl.getAudioInputControlServices(device).forEach { it.setGainSetting(request.gainSetting) } Empty.getDefaultInstance() } } override fun setMute(request: SetMuteRequest, responseObserver: StreamObserver<Empty>) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) Log.i(TAG, "setMute(${device}, ${request.mute})") bluetoothVolumeControl.getAudioInputControlServices(device).forEach { it.setMute(request.mute) } Empty.getDefaultInstance() } } override fun setGainMode(request: SetGainModeRequest, responseObserver: StreamObserver<Empty>) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) Log.i(TAG, "setMute(${device}, ${request.gainMode})") bluetoothVolumeControl.getAudioInputControlServices(device).forEach { it.setGainMode(request.gainMode) } Empty.getDefaultInstance() } } }
pandora/interfaces/pandora_experimental/vcp.proto +23 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,14 @@ service VCP { rpc SetVolumeOffset(SetVolumeOffsetRequest) returns (google.protobuf.Empty); // Wait for device to be connected. rpc WaitConnect(WaitConnectRequest) returns (google.protobuf.Empty); // TODO: AICS interfaces could be made a bit more generic by first fetching // what are the available instances and using them like the Connection object // is used. rpc SetGainSetting(SetGainSettingRequest) returns (google.protobuf.Empty); rpc SetMute(SetMuteRequest) returns (google.protobuf.Empty); rpc SetGainMode(SetGainModeRequest) returns (google.protobuf.Empty); } // Request of the `SetDeviceVolume` method Loading @@ -45,3 +53,18 @@ message SetVolumeOffsetRequest{ message WaitConnectRequest { Connection connection = 1; } message SetGainSettingRequest { Connection connection = 1; int32 gainSetting = 2; } message SetMuteRequest { Connection connection = 1; // See Mute.aidl for valid values int32 mute = 2; } message SetGainModeRequest { Connection connection = 1; // See GainMode.aidl for valid values int32 gainMode = 2; }