bt_test_harness/
low_energy_peripheral.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{Context as _, Error};
6use fidl_fuchsia_bluetooth_le::{ConnectionProxy, PeripheralEvent, PeripheralProxy};
7use fidl_fuchsia_hardware_bluetooth::EmulatorProxy;
8use fuchsia_bluetooth::expectation::asynchronous::{
9    expectable, Expectable, ExpectableExt, ExpectableState,
10};
11use fuchsia_bluetooth::types::le::Peer;
12use futures::future::{self, BoxFuture};
13use futures::{FutureExt, TryFutureExt, TryStreamExt};
14use std::ops::{Deref, DerefMut};
15use std::sync::Arc;
16use test_harness::{SharedState, TestHarness, SHARED_STATE_TEST_COMPONENT_INDEX};
17
18use crate::core_realm::{CoreRealm, SHARED_STATE_INDEX};
19use crate::emulator::{watch_advertising_states, EmulatorState};
20use crate::host_watcher::ActivatedFakeHost;
21
22/// A snapshot of the current LE peripheral procedure states of the controller.
23#[derive(Clone, Debug, Default)]
24pub struct PeripheralState {
25    emulator_state: EmulatorState,
26
27    /// Observed peer connections.
28    pub connections: Vec<(Peer, ConnectionProxy)>,
29}
30
31impl PeripheralState {
32    /// Resets to the default state.
33    pub fn reset(&mut self) {
34        self.emulator_state = EmulatorState::default();
35        self.connections.clear();
36    }
37}
38
39impl AsMut<EmulatorState> for PeripheralState {
40    fn as_mut(&mut self) -> &mut EmulatorState {
41        &mut self.emulator_state
42    }
43}
44
45impl AsRef<EmulatorState> for PeripheralState {
46    fn as_ref(&self) -> &EmulatorState {
47        &self.emulator_state
48    }
49}
50
51#[derive(Clone)]
52pub struct PeripheralHarness(Expectable<PeripheralState, Aux>);
53
54impl Deref for PeripheralHarness {
55    type Target = Expectable<PeripheralState, Aux>;
56
57    fn deref(&self) -> &Self::Target {
58        &self.0
59    }
60}
61
62impl DerefMut for PeripheralHarness {
63    fn deref_mut(&mut self) -> &mut Self::Target {
64        &mut self.0
65    }
66}
67
68/// Auxilliary data for the PeripheralHarness
69pub struct Aux {
70    pub peripheral: PeripheralProxy,
71    emulator: EmulatorProxy,
72}
73
74impl AsRef<EmulatorProxy> for Aux {
75    fn as_ref(&self) -> &EmulatorProxy {
76        &self.emulator
77    }
78}
79
80impl TestHarness for PeripheralHarness {
81    type Env = (ActivatedFakeHost, Arc<CoreRealm>);
82    type Runner = BoxFuture<'static, Result<(), Error>>;
83
84    fn init(
85        shared_state: &Arc<SharedState>,
86    ) -> BoxFuture<'static, Result<(Self, Self::Env, Self::Runner), Error>> {
87        let shared_state = shared_state.clone();
88        async move {
89            let test_component: Arc<String> = shared_state
90                .get(SHARED_STATE_TEST_COMPONENT_INDEX)
91                .expect("SharedState must have TEST-COMPONENT")?;
92            let inserter = move || CoreRealm::create(test_component.to_string());
93            let realm = shared_state.get_or_insert_with(SHARED_STATE_INDEX, inserter).await?;
94            let host = ActivatedFakeHost::new(realm.clone()).await?;
95            let peripheral = realm
96                .instance()
97                .connect_to_protocol_at_exposed_dir()
98                .context("Failed to connect to BLE Peripheral service")?;
99            let harness = PeripheralHarness(expectable(
100                Default::default(),
101                Aux { peripheral, emulator: host.emulator().clone() },
102            ));
103
104            // Create a task to process the state update watcher
105            let watch_adv = watch_advertising_states(harness.deref().clone());
106            let watch_conn = watch_connections(harness.clone());
107            let run_peripheral =
108                future::try_join(watch_adv, watch_conn).map_ok(|((), ())| ()).boxed();
109
110            Ok((harness, (host, realm), run_peripheral))
111        }
112        .boxed()
113    }
114    fn terminate((emulator, realm): Self::Env) -> BoxFuture<'static, Result<(), Error>> {
115        // The realm must be kept alive in order for emulator.release() to work properly.
116        async move {
117            let _realm = realm;
118            emulator.release().await
119        }
120        .boxed()
121    }
122}
123
124async fn watch_connections(harness: PeripheralHarness) -> Result<(), Error> {
125    let mut events = harness.aux().peripheral.take_event_stream();
126    while let Some(e) = events.try_next().await? {
127        match e {
128            PeripheralEvent::OnPeerConnected { peer, connection } => {
129                harness.write_state().connections.push((peer.try_into()?, connection.into_proxy()));
130            }
131        }
132        harness.notify_state_changed();
133    }
134    Ok(())
135}