Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 86734bc0 authored by Zach Johnson's avatar Zach Johnson
Browse files

rusty-gd: implement connection complete for classic acl

still not hooked up into the core dispatch yet, coming soon

Bug: 171749953
Tag: #gd-refactor
Test: gd/cert/run --rhost
Change-Id: I32c487719ab55cfb6996a308e4c3998a08c5a078
parent 677e01ab
Loading
Loading
Loading
Loading
+108 −13
Original line number Diff line number Diff line
//! Classic ACL manager

use bt_hci::{Address, CommandSender};
use bt_hci::{Address, CommandSender, EventRegistry};
use bt_packets::hci::EventChild::ConnectionComplete;
use bt_packets::hci::{
    ClockOffsetValid, CreateConnectionBuilder, CreateConnectionCancelBuilder,
    CreateConnectionRoleSwitch, PageScanRepetitionMode,
    CreateConnectionRoleSwitch, ErrorCode, EventCode, PageScanRepetitionMode, Role,
};
use gddi::{module, provides, Stoppable};
use log::warn;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::runtime::Runtime;
use tokio::select;
use tokio::sync::mpsc::{channel, Sender};
use tokio::sync::oneshot;
use tokio::sync::mpsc::{channel, Receiver, Sender};
use tokio::sync::{oneshot, Mutex};

module! {
    classic_acl_module,
@@ -23,6 +26,40 @@ module! {
#[derive(Clone, Stoppable)]
pub struct AclManager {
    req_tx: Sender<Request>,
    /// High level events from AclManager
    pub evt_rx: Arc<Mutex<Receiver<Event>>>,
}

/// Events generated by AclManager
#[derive(Debug)]
pub enum Event {
    /// Connection was successful - provides the newly created connection
    ConnectSuccess(Connection),
    /// Locally initialted connection was not successful - indicates address & reason
    ConnectFail {
        /// Address of the failed connection
        addr: Address,
        /// Reason of the failed connection
        reason: ErrorCode,
    },
}

/// A classic ACL connection
#[derive(Debug)]
pub struct Connection {
    addr: Address,
    shared: Arc<Mutex<ConnectionShared>>,
}

struct ConnectionInternal {
    addr: Address,
    #[allow(dead_code)]
    shared: Arc<Mutex<ConnectionShared>>,
}

#[derive(Debug)]
struct ConnectionShared {
    role: Role,
}

impl AclManager {
@@ -45,39 +82,97 @@ enum Request {
    CancelConnect { addr: Address, fut: oneshot::Sender<()> },
}

#[derive(Eq, PartialEq)]
enum PendingConnect {
    Outgoing(Address),
    #[allow(dead_code)]
    Incoming(Address),
    None,
}

impl PendingConnect {
    fn take(&mut self) -> Self {
        std::mem::replace(self, PendingConnect::None)
    }
}

#[provides]
async fn provide_acl_manager(mut hci: CommandSender, rt: Arc<Runtime>) -> AclManager {
async fn provide_acl_manager(
    mut hci: CommandSender,
    mut events: EventRegistry,
    rt: Arc<Runtime>,
) -> AclManager {
    let (req_tx, mut req_rx) = channel::<Request>(10);
    let (conn_evt_tx, conn_evt_rx) = channel::<Event>(10);

    rt.spawn(async move {
        let mut pending_connects: Vec<Address> = Vec::new();
        let mut outgoing_connect: Option<Address> = None;
        let mut connections: HashMap<u16, ConnectionInternal> = HashMap::new();
        let mut connect_queue: Vec<Address> = Vec::new();
        let mut pending = PendingConnect::None;

        let (evt_tx, mut evt_rx) = channel(3);
        events.register(EventCode::ConnectionComplete, evt_tx).await;

        loop {
            select! {
                Some(req) = req_rx.recv() => {
                    match req {
                        Request::Connect { addr } => {
                            if outgoing_connect.is_none() {
                                outgoing_connect = Some(addr);
                            if connections.values().any(|c| c.addr == addr) {
                                warn!("already connected: {}", addr);
                            }
                            if let PendingConnect::None = pending {
                                pending = PendingConnect::Outgoing(addr);
                                hci.send(build_create_connection(addr)).await;
                            } else {
                                pending_connects.insert(0, addr);
                                connect_queue.insert(0, addr);
                            }
                        },
                        Request::CancelConnect { addr, fut } => {
                            pending_connects.retain(|p| *p != addr);
                            if outgoing_connect == Some(addr) {
                            connect_queue.retain(|p| *p != addr);
                            if pending == PendingConnect::Outgoing(addr) {
                                hci.send(CreateConnectionCancelBuilder { bd_addr: addr }).await;
                            }
                            fut.send(()).unwrap();
                        }
                    }
                }
                Some(evt) = evt_rx.recv() => {
                    match evt.specialize() {
                        ConnectionComplete(evt) => {
                            let addr = evt.get_bd_addr();
                            let status = evt.get_status();
                            let handle = evt.get_connection_handle();
                            let role = match pending.take() {
                                PendingConnect::Outgoing(a) if a == addr => Role::Central,
                                PendingConnect::Incoming(a) if a == addr => Role::Peripheral,
                                _ => panic!("No prior connection request for {}", addr),
                            };

                            match status {
                                ErrorCode::Success => {
                                    let shared = Arc::new(Mutex::new(ConnectionShared { role }));
                                    assert!(connections.insert(
                                        handle,
                                        ConnectionInternal { addr, shared: shared.clone() }
                                    ).is_none());

                                    conn_evt_tx.send(Event::ConnectSuccess(Connection {
                                        addr,
                                        shared
                                    })).await.unwrap();
                                },
                                _ => conn_evt_tx.send(Event::ConnectFail { addr, reason: status }).await.unwrap(),
                            }
                        },
                        _ => unimplemented!(),
                    }
                }
            }
        }
    });

    AclManager { req_tx }
    AclManager { req_tx, evt_rx: Arc::new(Mutex::new(conn_evt_rx)) }
}

fn build_create_connection(bd_addr: Address) -> CreateConnectionBuilder {