mirror of
https://github.com/google/bumble.git
synced 2026-04-18 00:45:32 +00:00
wip
This commit is contained in:
@@ -1109,7 +1109,7 @@ class Central(Connection.Listener):
|
|||||||
transport,
|
transport,
|
||||||
peripheral_address,
|
peripheral_address,
|
||||||
classic,
|
classic,
|
||||||
role_factory,
|
scenario_factory,
|
||||||
mode_factory,
|
mode_factory,
|
||||||
connection_interval,
|
connection_interval,
|
||||||
phy,
|
phy,
|
||||||
@@ -1122,7 +1122,7 @@ class Central(Connection.Listener):
|
|||||||
self.transport = transport
|
self.transport = transport
|
||||||
self.peripheral_address = peripheral_address
|
self.peripheral_address = peripheral_address
|
||||||
self.classic = classic
|
self.classic = classic
|
||||||
self.role_factory = role_factory
|
self.scenario_factory = scenario_factory
|
||||||
self.mode_factory = mode_factory
|
self.mode_factory = mode_factory
|
||||||
self.authenticate = authenticate
|
self.authenticate = authenticate
|
||||||
self.encrypt = encrypt or authenticate
|
self.encrypt = encrypt or authenticate
|
||||||
@@ -1175,7 +1175,7 @@ class Central(Connection.Listener):
|
|||||||
DEFAULT_CENTRAL_NAME, central_address, hci_source, hci_sink
|
DEFAULT_CENTRAL_NAME, central_address, hci_source, hci_sink
|
||||||
)
|
)
|
||||||
mode = self.mode_factory(self.device)
|
mode = self.mode_factory(self.device)
|
||||||
role = self.role_factory(mode)
|
scenario = self.scenario_factory(mode)
|
||||||
self.device.classic_enabled = self.classic
|
self.device.classic_enabled = self.classic
|
||||||
|
|
||||||
# Set up a pairing config factory with minimal requirements.
|
# Set up a pairing config factory with minimal requirements.
|
||||||
@@ -1256,7 +1256,7 @@ class Central(Connection.Listener):
|
|||||||
|
|
||||||
await mode.on_connection(self.connection)
|
await mode.on_connection(self.connection)
|
||||||
|
|
||||||
await role.run()
|
await scenario.run()
|
||||||
await asyncio.sleep(DEFAULT_LINGER_TIME)
|
await asyncio.sleep(DEFAULT_LINGER_TIME)
|
||||||
await self.connection.disconnect()
|
await self.connection.disconnect()
|
||||||
|
|
||||||
@@ -1287,7 +1287,7 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
transport,
|
transport,
|
||||||
role_factory,
|
scenario_factory,
|
||||||
mode_factory,
|
mode_factory,
|
||||||
classic,
|
classic,
|
||||||
extended_data_length,
|
extended_data_length,
|
||||||
@@ -1295,11 +1295,11 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|||||||
):
|
):
|
||||||
self.transport = transport
|
self.transport = transport
|
||||||
self.classic = classic
|
self.classic = classic
|
||||||
self.role_factory = role_factory
|
self.scenario_factory = scenario_factory
|
||||||
self.mode_factory = mode_factory
|
self.mode_factory = mode_factory
|
||||||
self.extended_data_length = extended_data_length
|
self.extended_data_length = extended_data_length
|
||||||
self.role_switch = role_switch
|
self.role_switch = role_switch
|
||||||
self.role = None
|
self.scenario = None
|
||||||
self.mode = None
|
self.mode = None
|
||||||
self.device = None
|
self.device = None
|
||||||
self.connection = None
|
self.connection = None
|
||||||
@@ -1319,7 +1319,7 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|||||||
)
|
)
|
||||||
self.device.listener = self
|
self.device.listener = self
|
||||||
self.mode = self.mode_factory(self.device)
|
self.mode = self.mode_factory(self.device)
|
||||||
self.role = self.role_factory(self.mode)
|
self.scenario = self.scenario_factory(self.mode)
|
||||||
self.device.classic_enabled = self.classic
|
self.device.classic_enabled = self.classic
|
||||||
|
|
||||||
# Set up a pairing config factory with minimal requirements.
|
# Set up a pairing config factory with minimal requirements.
|
||||||
@@ -1356,7 +1356,7 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|||||||
print_connection(self.connection)
|
print_connection(self.connection)
|
||||||
|
|
||||||
await self.mode.on_connection(self.connection)
|
await self.mode.on_connection(self.connection)
|
||||||
await self.role.run()
|
await self.scenario.run()
|
||||||
await asyncio.sleep(DEFAULT_LINGER_TIME)
|
await asyncio.sleep(DEFAULT_LINGER_TIME)
|
||||||
|
|
||||||
def on_connection(self, connection):
|
def on_connection(self, connection):
|
||||||
@@ -1385,7 +1385,7 @@ class Peripheral(Device.Listener, Connection.Listener):
|
|||||||
def on_disconnection(self, reason):
|
def on_disconnection(self, reason):
|
||||||
logging.info(color(f'!!! Disconnection: reason={reason}', 'red'))
|
logging.info(color(f'!!! Disconnection: reason={reason}', 'red'))
|
||||||
self.connection = None
|
self.connection = None
|
||||||
self.role.reset()
|
self.scenario.reset()
|
||||||
|
|
||||||
if self.classic:
|
if self.classic:
|
||||||
AsyncRunner.spawn(self.device.set_discoverable(True))
|
AsyncRunner.spawn(self.device.set_discoverable(True))
|
||||||
@@ -1467,13 +1467,13 @@ def create_mode_factory(ctx, default_mode):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
def create_role_factory(ctx, default_role):
|
def create_scenario_factory(ctx, default_scenario):
|
||||||
role = ctx.obj['role']
|
scenario = ctx.obj['scenario']
|
||||||
if role is None:
|
if scenario is None:
|
||||||
role = default_role
|
scenarion = default_scenario
|
||||||
|
|
||||||
def create_role(packet_io):
|
def create_scenario(packet_io):
|
||||||
if role == 'sender':
|
if scenario == 'send':
|
||||||
return Sender(
|
return Sender(
|
||||||
packet_io,
|
packet_io,
|
||||||
start_delay=ctx.obj['start_delay'],
|
start_delay=ctx.obj['start_delay'],
|
||||||
@@ -1484,10 +1484,10 @@ def create_role_factory(ctx, default_role):
|
|||||||
packet_count=ctx.obj['packet_count'],
|
packet_count=ctx.obj['packet_count'],
|
||||||
)
|
)
|
||||||
|
|
||||||
if role == 'receiver':
|
if scenario == 'receive':
|
||||||
return Receiver(packet_io, ctx.obj['linger'])
|
return Receiver(packet_io, ctx.obj['linger'])
|
||||||
|
|
||||||
if role == 'ping':
|
if scenario == 'ping':
|
||||||
return Ping(
|
return Ping(
|
||||||
packet_io,
|
packet_io,
|
||||||
start_delay=ctx.obj['start_delay'],
|
start_delay=ctx.obj['start_delay'],
|
||||||
@@ -1498,12 +1498,12 @@ def create_role_factory(ctx, default_role):
|
|||||||
packet_count=ctx.obj['packet_count'],
|
packet_count=ctx.obj['packet_count'],
|
||||||
)
|
)
|
||||||
|
|
||||||
if role == 'pong':
|
if scenario == 'pong':
|
||||||
return Pong(packet_io, ctx.obj['linger'])
|
return Pong(packet_io, ctx.obj['linger'])
|
||||||
|
|
||||||
raise ValueError('invalid role')
|
raise ValueError('invalid scenario')
|
||||||
|
|
||||||
return create_role
|
return create_scenario
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -1511,7 +1511,7 @@ def create_role_factory(ctx, default_role):
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@click.group()
|
@click.group()
|
||||||
@click.option('--device-config', metavar='FILENAME', help='Device configuration file')
|
@click.option('--device-config', metavar='FILENAME', help='Device configuration file')
|
||||||
@click.option('--role', type=click.Choice(['sender', 'receiver', 'ping', 'pong']))
|
@click.option('--scenario', type=click.Choice(['send', 'receive', 'ping', 'pong']))
|
||||||
@click.option(
|
@click.option(
|
||||||
'--mode',
|
'--mode',
|
||||||
type=click.Choice(
|
type=click.Choice(
|
||||||
@@ -1606,7 +1606,7 @@ def create_role_factory(ctx, default_role):
|
|||||||
metavar='SIZE',
|
metavar='SIZE',
|
||||||
type=click.IntRange(8, 8192),
|
type=click.IntRange(8, 8192),
|
||||||
default=500,
|
default=500,
|
||||||
help='Packet size (client or ping role)',
|
help='Packet size (send or ping scenario)',
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
'--packet-count',
|
'--packet-count',
|
||||||
@@ -1614,7 +1614,7 @@ def create_role_factory(ctx, default_role):
|
|||||||
metavar='COUNT',
|
metavar='COUNT',
|
||||||
type=int,
|
type=int,
|
||||||
default=10,
|
default=10,
|
||||||
help='Packet count (client or ping role)',
|
help='Packet count (send or ping scenario)',
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
'--start-delay',
|
'--start-delay',
|
||||||
@@ -1622,7 +1622,7 @@ def create_role_factory(ctx, default_role):
|
|||||||
metavar='SECONDS',
|
metavar='SECONDS',
|
||||||
type=int,
|
type=int,
|
||||||
default=1,
|
default=1,
|
||||||
help='Start delay (client or ping role)',
|
help='Start delay (send or ping scenario)',
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
'--repeat',
|
'--repeat',
|
||||||
@@ -1630,7 +1630,7 @@ def create_role_factory(ctx, default_role):
|
|||||||
type=int,
|
type=int,
|
||||||
default=0,
|
default=0,
|
||||||
help=(
|
help=(
|
||||||
'Repeat the run N times (client and ping roles)'
|
'Repeat the run N times (send and ping scenario)'
|
||||||
'(0, which is the fault, to run just once) '
|
'(0, which is the fault, to run just once) '
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -1654,13 +1654,13 @@ def create_role_factory(ctx, default_role):
|
|||||||
@click.option(
|
@click.option(
|
||||||
'--linger',
|
'--linger',
|
||||||
is_flag=True,
|
is_flag=True,
|
||||||
help="Don't exit at the end of a run (server and pong roles)",
|
help="Don't exit at the end of a run (receive and pong scenarios)",
|
||||||
)
|
)
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def bench(
|
def bench(
|
||||||
ctx,
|
ctx,
|
||||||
device_config,
|
device_config,
|
||||||
role,
|
scenario,
|
||||||
mode,
|
mode,
|
||||||
att_mtu,
|
att_mtu,
|
||||||
extended_data_length,
|
extended_data_length,
|
||||||
@@ -1686,7 +1686,7 @@ def bench(
|
|||||||
):
|
):
|
||||||
ctx.ensure_object(dict)
|
ctx.ensure_object(dict)
|
||||||
ctx.obj['device_config'] = device_config
|
ctx.obj['device_config'] = device_config
|
||||||
ctx.obj['role'] = role
|
ctx.obj['scenario'] = scenario
|
||||||
ctx.obj['mode'] = mode
|
ctx.obj['mode'] = mode
|
||||||
ctx.obj['att_mtu'] = att_mtu
|
ctx.obj['att_mtu'] = att_mtu
|
||||||
ctx.obj['rfcomm_channel'] = rfcomm_channel
|
ctx.obj['rfcomm_channel'] = rfcomm_channel
|
||||||
@@ -1740,7 +1740,7 @@ def central(
|
|||||||
ctx, transport, peripheral_address, connection_interval, phy, authenticate, encrypt
|
ctx, transport, peripheral_address, connection_interval, phy, authenticate, encrypt
|
||||||
):
|
):
|
||||||
"""Run as a central (initiates the connection)"""
|
"""Run as a central (initiates the connection)"""
|
||||||
role_factory = create_role_factory(ctx, 'sender')
|
scenario_factory = create_scenario_factory(ctx, 'send')
|
||||||
mode_factory = create_mode_factory(ctx, 'gatt-client')
|
mode_factory = create_mode_factory(ctx, 'gatt-client')
|
||||||
classic = ctx.obj['classic']
|
classic = ctx.obj['classic']
|
||||||
|
|
||||||
@@ -1749,7 +1749,7 @@ def central(
|
|||||||
transport,
|
transport,
|
||||||
peripheral_address,
|
peripheral_address,
|
||||||
classic,
|
classic,
|
||||||
role_factory,
|
scenario_factory,
|
||||||
mode_factory,
|
mode_factory,
|
||||||
connection_interval,
|
connection_interval,
|
||||||
phy,
|
phy,
|
||||||
@@ -1767,13 +1767,13 @@ def central(
|
|||||||
@click.pass_context
|
@click.pass_context
|
||||||
def peripheral(ctx, transport):
|
def peripheral(ctx, transport):
|
||||||
"""Run as a peripheral (waits for a connection)"""
|
"""Run as a peripheral (waits for a connection)"""
|
||||||
role_factory = create_role_factory(ctx, 'receiver')
|
scenario_factory = create_scenario_factory(ctx, 'receive')
|
||||||
mode_factory = create_mode_factory(ctx, 'gatt-server')
|
mode_factory = create_mode_factory(ctx, 'gatt-server')
|
||||||
|
|
||||||
async def run_peripheral():
|
async def run_peripheral():
|
||||||
await Peripheral(
|
await Peripheral(
|
||||||
transport,
|
transport,
|
||||||
role_factory,
|
scenario_factory,
|
||||||
mode_factory,
|
mode_factory,
|
||||||
ctx.obj['classic'],
|
ctx.obj['classic'],
|
||||||
ctx.obj['extended_data_length'],
|
ctx.obj['extended_data_length'],
|
||||||
|
|||||||
@@ -11,32 +11,44 @@ Usage: bumble-bench [OPTIONS] COMMAND [ARGS]...
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
--device-config FILENAME Device configuration file
|
--device-config FILENAME Device configuration file
|
||||||
--role [sender|receiver|ping|pong]
|
--scenario [send|receive|ping|pong]
|
||||||
--mode [gatt-client|gatt-server|l2cap-client|l2cap-server|rfcomm-client|rfcomm-server]
|
--mode [gatt-client|gatt-server|l2cap-client|l2cap-server|rfcomm-client|rfcomm-server]
|
||||||
--att-mtu MTU GATT MTU (gatt-client mode) [23<=x<=517]
|
--att-mtu MTU GATT MTU (gatt-client mode) [23<=x<=517]
|
||||||
--extended-data-length TEXT Request a data length upon connection,
|
--extended-data-length TEXT Request a data length upon connection,
|
||||||
specified as tx_octets/tx_time
|
specified as tx_octets/tx_time
|
||||||
--rfcomm-channel INTEGER RFComm channel to use
|
--role-switch [central|peripheral]
|
||||||
|
Request role switch upon connection (central
|
||||||
|
or peripheral)
|
||||||
|
--rfcomm-channel INTEGER RFComm channel to use (specify 0 for channel
|
||||||
|
discovery via SDP)
|
||||||
--rfcomm-uuid TEXT RFComm service UUID to use (ignored if
|
--rfcomm-uuid TEXT RFComm service UUID to use (ignored if
|
||||||
--rfcomm-channel is not 0)
|
--rfcomm-channel is not 0)
|
||||||
|
--rfcomm-l2cap-mtu INTEGER RFComm L2CAP MTU
|
||||||
|
--rfcomm-max-frame-size INTEGER
|
||||||
|
RFComm maximum frame size
|
||||||
|
--rfcomm-initial-credits INTEGER
|
||||||
|
RFComm initial credits
|
||||||
|
--rfcomm-max-credits INTEGER RFComm max credits
|
||||||
|
--rfcomm-credits-threshold INTEGER
|
||||||
|
RFComm credits threshold
|
||||||
--l2cap-psm INTEGER L2CAP PSM to use
|
--l2cap-psm INTEGER L2CAP PSM to use
|
||||||
--l2cap-mtu INTEGER L2CAP MTU to use
|
--l2cap-mtu INTEGER L2CAP MTU to use
|
||||||
--l2cap-mps INTEGER L2CAP MPS to use
|
--l2cap-mps INTEGER L2CAP MPS to use
|
||||||
--l2cap-max-credits INTEGER L2CAP maximum number of credits allowed for
|
--l2cap-max-credits INTEGER L2CAP maximum number of credits allowed for
|
||||||
the peer
|
the peer
|
||||||
-s, --packet-size SIZE Packet size (client or ping role)
|
-s, --packet-size SIZE Packet size (send or ping scenario)
|
||||||
[8<=x<=4096]
|
[8<=x<=8192]
|
||||||
-c, --packet-count COUNT Packet count (client or ping role)
|
-c, --packet-count COUNT Packet count (send or ping scenario)
|
||||||
-sd, --start-delay SECONDS Start delay (client or ping role)
|
-sd, --start-delay SECONDS Start delay (send or ping scenario)
|
||||||
--repeat N Repeat the run N times (client and ping
|
--repeat N Repeat the run N times (send and ping
|
||||||
roles)(0, which is the fault, to run just
|
scenario)(0, which is the fault, to run just
|
||||||
once)
|
once)
|
||||||
--repeat-delay SECONDS Delay, in seconds, between repeats
|
--repeat-delay SECONDS Delay, in seconds, between repeats
|
||||||
--pace MILLISECONDS Wait N milliseconds between packets (0,
|
--pace MILLISECONDS Wait N milliseconds between packets (0,
|
||||||
which is the fault, to send as fast as
|
which is the fault, to send as fast as
|
||||||
possible)
|
possible)
|
||||||
--linger Don't exit at the end of a run (server and
|
--linger Don't exit at the end of a run (receive and
|
||||||
pong roles)
|
pong scenarios)
|
||||||
--help Show this message and exit.
|
--help Show this message and exit.
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
@@ -82,7 +94,7 @@ Device 1 mode | Device 2 mode
|
|||||||
|
|
||||||
Device 1 role | Device 2 role
|
Device 1 role | Device 2 role
|
||||||
--------------|--------------
|
--------------|--------------
|
||||||
``sender`` | ``receiver``
|
``send`` | ``receive``
|
||||||
``ping`` | ``pong``
|
``ping`` | ``pong``
|
||||||
|
|
||||||
|
|
||||||
@@ -137,12 +149,12 @@ the other on `usb:1`, and two consoles/terminals. We will run a command in each.
|
|||||||
!!! example "Ping/Pong Latency"
|
!!! example "Ping/Pong Latency"
|
||||||
In the first console/terminal:
|
In the first console/terminal:
|
||||||
```
|
```
|
||||||
$ bumble-bench --role pong peripheral usb:0
|
$ bumble-bench --scenario pong peripheral usb:0
|
||||||
```
|
```
|
||||||
|
|
||||||
In the second console/terminal:
|
In the second console/terminal:
|
||||||
```
|
```
|
||||||
$ bumble-bench --role ping central usb:1
|
$ bumble-bench --scenario ping central usb:1
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! example "Reversed modes with GATT and custom connection interval"
|
!!! example "Reversed modes with GATT and custom connection interval"
|
||||||
@@ -170,10 +182,10 @@ the other on `usb:1`, and two consoles/terminals. We will run a command in each.
|
|||||||
!!! example "Reversed roles with L2CAP"
|
!!! example "Reversed roles with L2CAP"
|
||||||
In the first console/terminal:
|
In the first console/terminal:
|
||||||
```
|
```
|
||||||
$ bumble-bench --mode l2cap-client --role sender peripheral usb:0
|
$ bumble-bench --mode l2cap-client --scenario send peripheral usb:0
|
||||||
```
|
```
|
||||||
|
|
||||||
In the second console/terminal:
|
In the second console/terminal:
|
||||||
```
|
```
|
||||||
$ bumble-bench --mode l2cap-server --role receiver central usb:1
|
$ bumble-bench --mode l2cap-server --scenario receive central usb:1
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -17,86 +17,113 @@ package com.github.google.bumble.btbench;
|
|||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothManager;
|
import android.bluetooth.BluetoothManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.renderscript.RSInvalidStateException;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.google.android.mobly.snippet.Snippet;
|
import com.google.android.mobly.snippet.Snippet;
|
||||||
import com.google.android.mobly.snippet.rpc.Rpc;
|
import com.google.android.mobly.snippet.rpc.Rpc;
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
|
||||||
public class AutomationSnippet implements Snippet {
|
public class AutomationSnippet implements Snippet {
|
||||||
private static final String TAG = "btbench.snippet";
|
private static final String TAG = "btbench.snippet";
|
||||||
private final BluetoothAdapter mBluetoothAdapter;
|
private final BluetoothAdapter mBluetoothAdapter;
|
||||||
private AppViewModel rfcommServerModel;
|
private final Context mContext;
|
||||||
private RfcommServer rfcommServer;
|
|
||||||
private AppViewModel l2capServerModel;
|
|
||||||
private L2capServer l2capServer;
|
|
||||||
|
|
||||||
public AutomationSnippet() {
|
public AutomationSnippet() {
|
||||||
Context context = ApplicationProvider.getApplicationContext();
|
mContext = ApplicationProvider.getApplicationContext();
|
||||||
BluetoothManager bluetoothManager = context.getSystemService(BluetoothManager.class);
|
BluetoothManager bluetoothManager = mContext.getSystemService(BluetoothManager.class);
|
||||||
mBluetoothAdapter = bluetoothManager.getAdapter();
|
mBluetoothAdapter = bluetoothManager.getAdapter();
|
||||||
if (mBluetoothAdapter == null) {
|
if (mBluetoothAdapter == null) {
|
||||||
throw new RuntimeException("bluetooth not supported");
|
throw new RuntimeException("bluetooth not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JSONObject throughputStats(AppViewModel model) throws JSONException {
|
private void runScenario(AppViewModel model, String mode, String scenario) {
|
||||||
|
Mode runner;
|
||||||
|
switch (mode) {
|
||||||
|
case "rfcomm-client":
|
||||||
|
runner = new RfcommClient(model, mBluetoothAdapter, (PacketIO packetIO) -> createIoClient(model, scenario, packetIO));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "rfcomm-server":
|
||||||
|
runner = new RfcommServer(model, mBluetoothAdapter, (PacketIO packetIO) -> createIoClient(model, scenario, packetIO));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "l2cap-client":
|
||||||
|
runner = new L2capClient(model, mBluetoothAdapter, mContext, (PacketIO packetIO) -> createIoClient(model, scenario, packetIO));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "l2cap-server":
|
||||||
|
runner = new L2capServer(model, mBluetoothAdapter, (PacketIO packetIO) -> createIoClient(model, scenario, packetIO));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
runner.run(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IoClient createIoClient(AppViewModel model, String scenario, PacketIO packetIO) {
|
||||||
|
switch (scenario) {
|
||||||
|
case "send":
|
||||||
|
return new Sender(model, packetIO);
|
||||||
|
|
||||||
|
case "receive":
|
||||||
|
return new Receiver(model, packetIO);
|
||||||
|
|
||||||
|
case "ping":
|
||||||
|
return new Pinger(model, packetIO);
|
||||||
|
|
||||||
|
case "pong":
|
||||||
|
return new Ponger(model, packetIO);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JSONObject resultFromModel(AppViewModel model) throws JSONException {
|
||||||
JSONObject result = new JSONObject();
|
JSONObject result = new JSONObject();
|
||||||
JSONObject stats = new JSONObject();
|
JSONObject stats = new JSONObject();
|
||||||
result.put("stats", stats);
|
result.put("stats", stats);
|
||||||
JSONObject throughputStats = new JSONObject();
|
JSONObject throughputStats = new JSONObject();
|
||||||
stats.put("throughput", throughputStats);
|
stats.put("throughput", throughputStats);
|
||||||
throughputStats.put("average", model.getThroughput());
|
throughputStats.put("average", model.getThroughput());
|
||||||
|
JSONObject rttStats = new JSONObject();
|
||||||
|
stats.put("rtt", rttStats);
|
||||||
|
rttStats.put("compound", model.getStats());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Rpc(description = "Run an RFComm client throughput test")
|
@Rpc(description = "Run a scenario in RFComm Client mode")
|
||||||
public JSONObject runRfcommClient(String peerBluetoothAddress, int packetCount, int packetSize) throws JSONException {
|
public JSONObject runRfcommClient(String scenario, String peerBluetoothAddress, int packetCount, int packetSize, int packetInterval) throws JSONException {
|
||||||
assert(mBluetoothAdapter != null);
|
assert (mBluetoothAdapter != null);
|
||||||
AppViewModel model = new AppViewModel();
|
AppViewModel model = new AppViewModel();
|
||||||
model.setPeerBluetoothAddress(peerBluetoothAddress);
|
model.setPeerBluetoothAddress(peerBluetoothAddress);
|
||||||
model.setSenderPacketCount(packetCount);
|
model.setSenderPacketCount(packetCount);
|
||||||
model.setSenderPacketSize(packetSize);
|
model.setSenderPacketSize(packetSize);
|
||||||
|
model.setSenderPacketInterval(packetInterval);
|
||||||
|
|
||||||
//RfcommClient rfCommClient = new RfcommClient(model, mBluetoothAdapter);
|
runScenario(model, "rfcomm-client", scenario);
|
||||||
//rfCommClient.run(true);
|
return resultFromModel(model);
|
||||||
return throughputStats(model);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Rpc(description = "Run an L2CAP client throughput test")
|
@Rpc(description = "Run a scenario in L2CAP Client mode")
|
||||||
public JSONObject runL2capClient(String peerBluetoothAddress, int psm, boolean use_2m_phy, int packetCount, int packetSize) throws JSONException {
|
public JSONObject runL2capClient(String scenario, String peerBluetoothAddress, int psm, boolean use_2m_phy, int packetCount, int packetSize, int packetInterval) throws JSONException {
|
||||||
assert(mBluetoothAdapter != null);
|
assert (mBluetoothAdapter != null);
|
||||||
AppViewModel model = new AppViewModel();
|
AppViewModel model = new AppViewModel();
|
||||||
model.setPeerBluetoothAddress(peerBluetoothAddress);
|
model.setPeerBluetoothAddress(peerBluetoothAddress);
|
||||||
model.setL2capPsm(psm);
|
model.setL2capPsm(psm);
|
||||||
model.setUse2mPhy(use_2m_phy);
|
model.setUse2mPhy(use_2m_phy);
|
||||||
model.setSenderPacketCount(packetCount);
|
model.setSenderPacketCount(packetCount);
|
||||||
model.setSenderPacketSize(packetSize);
|
model.setSenderPacketSize(packetSize);
|
||||||
|
model.setSenderPacketInterval(packetInterval);
|
||||||
|
|
||||||
Context context = ApplicationProvider.getApplicationContext();
|
runScenario(model, "l2cap-client", scenario);
|
||||||
//L2capClient l2capClient = new L2capClient(model, mBluetoothAdapter, context);
|
return resultFromModel(model);
|
||||||
//l2capClient.run(true);
|
|
||||||
return throughputStats(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Rpc(description = "Run an RFComm server")
|
|
||||||
public JSONObject runRfcommServer() throws JSONException {
|
|
||||||
assert(mBluetoothAdapter != null);
|
|
||||||
if (rfcommServerModel != null) {
|
|
||||||
rfcommServerModel.abort();
|
|
||||||
rfcommServerModel = null;
|
|
||||||
rfcommServer = null;
|
|
||||||
}
|
|
||||||
rfcommServerModel = new AppViewModel();
|
|
||||||
//rfcommServer = new RfcommServer(rfcommServerModel, mBluetoothAdapter);
|
|
||||||
//rfcommServer.run(true);
|
|
||||||
|
|
||||||
return new JSONObject();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -168,12 +168,25 @@ class MainActivity : ComponentActivity() {
|
|||||||
appViewModel.senderPacketInterval = packetInterval
|
appViewModel.senderPacketInterval = packetInterval
|
||||||
}
|
}
|
||||||
appViewModel.updateSenderPacketSizeSlider()
|
appViewModel.updateSenderPacketSizeSlider()
|
||||||
|
intent.getStringExtra("scenario")?.let {
|
||||||
|
when (it) {
|
||||||
|
"send" -> appViewModel.scenario = SEND_SCENARIO
|
||||||
|
"receive" -> appViewModel.scenario = RECEIVE_SCENARIO
|
||||||
|
"ping" -> appViewModel.scenario = PING_SCENARIO
|
||||||
|
"pong" -> appViewModel.scenario = PONG_SCENARIO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intent.getStringExtra("mode")?.let {
|
||||||
|
when (it) {
|
||||||
|
"rfcomm-client" -> appViewModel.mode = RFCOMM_CLIENT_MODE
|
||||||
|
"rfcomm-server" -> appViewModel.mode = RFCOMM_SERVER_MODE
|
||||||
|
"l2cap-client" -> appViewModel.mode = L2CAP_CLIENT_MODE
|
||||||
|
"l2cap-server" -> appViewModel.mode = L2CAP_SERVER_MODE
|
||||||
|
}
|
||||||
|
}
|
||||||
intent.getStringExtra("autostart")?.let {
|
intent.getStringExtra("autostart")?.let {
|
||||||
when (it) {
|
when (it) {
|
||||||
"rfcomm-client" -> runRfcommClient()
|
"run-scenario" -> runScenario()
|
||||||
"rfcomm-server" -> runRfcommServer()
|
|
||||||
"l2cap-client" -> runL2capClient()
|
|
||||||
"l2cap-server" -> runL2capServer()
|
|
||||||
"scan-start" -> runScan(true)
|
"scan-start" -> runScan(true)
|
||||||
"stop-start" -> runScan(false)
|
"stop-start" -> runScan(false)
|
||||||
}
|
}
|
||||||
@@ -200,6 +213,11 @@ class MainActivity : ComponentActivity() {
|
|||||||
runner.run(false)
|
runner.run(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun runScan(startScan: Boolean) {
|
||||||
|
val scan = bluetoothAdapter?.let { Scan(it) }
|
||||||
|
scan?.run(startScan)
|
||||||
|
}
|
||||||
|
|
||||||
private fun createIoClient(packetIo: PacketIO): IoClient {
|
private fun createIoClient(packetIo: PacketIO): IoClient {
|
||||||
return when (appViewModel.scenario) {
|
return when (appViewModel.scenario) {
|
||||||
SEND_SCENARIO -> Sender(appViewModel, packetIo)
|
SEND_SCENARIO -> Sender(appViewModel, packetIo)
|
||||||
@@ -210,30 +228,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun runRfcommClient() {
|
|
||||||
// val rfcommClient = bluetoothAdapter?.let { RfcommClient(appViewModel, it) }
|
|
||||||
// rfcommClient?.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun runRfcommServer() {
|
|
||||||
// val rfcommServer = bluetoothAdapter?.let { RfcommServer(appViewModel, it) }
|
|
||||||
// rfcommServer?.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun runL2capClient() {
|
|
||||||
// val l2capClient = bluetoothAdapter?.let { L2capClient(appViewModel, it, baseContext) }
|
|
||||||
// l2capClient?.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun runL2capServer() {
|
|
||||||
// val l2capServer = bluetoothAdapter?.let { L2capServer(appViewModel, it) }
|
|
||||||
// l2capServer?.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun runScan(startScan: Boolean) {
|
|
||||||
val scan = bluetoothAdapter?.let { Scan(it) }
|
|
||||||
scan?.run(startScan)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
fun becomeDiscoverable() {
|
fun becomeDiscoverable() {
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class SocketClient(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
ioClient.run()
|
ioClient.run()
|
||||||
|
socket.close()
|
||||||
} catch (error: IOException) {
|
} catch (error: IOException) {
|
||||||
Log.info("run ended abruptly")
|
Log.info("run ended abruptly")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public class HciProxy {
|
|||||||
HciHal hciHal = HciHal.create(new HciHalCallback() {
|
HciHal hciHal = HciHal.create(new HciHalCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPacket(HciPacket.Type type, byte[] packet) {
|
public void onPacket(HciPacket.Type type, byte[] packet) {
|
||||||
|
Log.d(TAG, String.format("CONTROLLER->HOST: type=%s, size=%d", type, packet.length));
|
||||||
mServer.sendPacket(type, packet);
|
mServer.sendPacket(type, packet);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -83,7 +84,7 @@ public class HciProxy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPacket(HciPacket.Type type, byte[] packet) {
|
public void onPacket(HciPacket.Type type, byte[] packet) {
|
||||||
Log.d(TAG, String.format("onPacket: type=%s, size=%d", type, packet.length));
|
Log.d(TAG, String.format("HOST->CONTROLLER: type=%s, size=%d", type, packet.length));
|
||||||
hciHal.sendPacket(type, packet);
|
hciHal.sendPacket(type, packet);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|||||||
Reference in New Issue
Block a user