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

Commit ce2eec7a authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "floss: media: qual: Allow partial profiles connection" am: 6eaaba6a

parents d05e4320 6eaaba6a
Loading
Loading
Loading
Loading
+76 −3
Original line number Diff line number Diff line
@@ -218,6 +218,7 @@ impl BluetoothAudioDevice {
pub enum MediaActions {
    Connect(String),
    Disconnect(String),
    ForceEnterConnected(String), // Only used for qualification.
}

#[derive(Debug, Clone, PartialEq)]
@@ -343,7 +344,7 @@ impl BluetoothMedia {

        self.connected_profiles.entry(addr).or_insert_with(HashSet::new).remove(&profile);

        if is_profile_critical {
        if is_profile_critical && self.is_complete_profiles_required() {
            self.notify_critical_profile_disconnected(addr);
        }

@@ -452,8 +453,10 @@ impl BluetoothMedia {
                        self.a2dp_caps.remove(&addr);
                        self.a2dp_audio_state.remove(&addr);
                        self.rm_connected_profile(addr, uuid::Profile::A2dpSink, true);
                        if self.is_complete_profiles_required() {
                            self.disconnect(addr.to_string());
                        }
                    }
                    _ => {
                        self.a2dp_states.insert(addr, state);
                    }
@@ -581,6 +584,7 @@ impl BluetoothMedia {
        match action {
            MediaActions::Connect(address) => self.connect(address),
            MediaActions::Disconnect(address) => self.disconnect(address),
            MediaActions::ForceEnterConnected(address) => self.force_enter_connected(address),
        }
    }

@@ -624,8 +628,10 @@ impl BluetoothMedia {
                        self.hfp_cap.remove(&addr);
                        self.hfp_audio_state.remove(&addr);
                        self.rm_connected_profile(addr, uuid::Profile::Hfp, true);
                        if self.is_complete_profiles_required() {
                            self.disconnect(addr.to_string());
                        }
                    }
                    BthfConnectionState::Connecting => {
                        info!("[{}]: hfp connecting.", DisplayAddress(&addr));
                    }
@@ -915,6 +921,18 @@ impl BluetoothMedia {
        let _ = txl.send(Message::Media(MediaActions::Disconnect(addr.to_string()))).await;
    }

    async fn wait_force_enter_connected(
        txl: &Sender<Message>,
        addr: &RawAddress,
        first_conn_ts: Instant,
    ) {
        let now_ts = Instant::now();
        let total_duration = Duration::from_secs(CONNECT_MISSING_PROFILES_TIMEOUT_SEC);
        let sleep_duration = (first_conn_ts + total_duration).saturating_duration_since(now_ts);
        sleep(sleep_duration).await;
        let _ = txl.send(Message::Media(MediaActions::ForceEnterConnected(addr.to_string()))).await;
    }

    fn notify_media_capability_updated(&mut self, addr: RawAddress) {
        let mut guard = self.fallback_tasks.lock().unwrap();
        let mut states = self.device_states.lock().unwrap();
@@ -980,11 +998,16 @@ impl BluetoothMedia {
        let device_states = self.device_states.clone();
        let txl = self.tx.clone();
        let ts = first_conn_ts;
        let is_complete_profiles_required = self.is_complete_profiles_required();
        match states.get(&addr).unwrap() {
            DeviceConnectionStates::Initiating => {
                let task = topstack::get_runtime().spawn(async move {
                    // As initiator we can just immediately start connecting
                    let _ = txl.send(Message::Media(MediaActions::Connect(addr.to_string()))).await;
                    if !is_complete_profiles_required {
                        BluetoothMedia::wait_force_enter_connected(&txl, &addr, ts).await;
                        return;
                    }
                    BluetoothMedia::wait_retry(&tasks, &device_states, &txl, &addr, ts).await;
                    BluetoothMedia::wait_disconnect(&tasks, &device_states, &txl, &addr, ts).await;
                });
@@ -992,6 +1015,10 @@ impl BluetoothMedia {
            }
            DeviceConnectionStates::ConnectingBeforeRetry => {
                let task = topstack::get_runtime().spawn(async move {
                    if !is_complete_profiles_required {
                        BluetoothMedia::wait_force_enter_connected(&txl, &addr, ts).await;
                        return;
                    }
                    BluetoothMedia::wait_retry(&tasks, &device_states, &txl, &addr, ts).await;
                    BluetoothMedia::wait_disconnect(&tasks, &device_states, &txl, &addr, ts).await;
                });
@@ -999,6 +1026,10 @@ impl BluetoothMedia {
            }
            DeviceConnectionStates::ConnectingAfterRetry => {
                let task = topstack::get_runtime().spawn(async move {
                    if !is_complete_profiles_required {
                        BluetoothMedia::wait_force_enter_connected(&txl, &addr, ts).await;
                        return;
                    }
                    BluetoothMedia::wait_disconnect(&tasks, &device_states, &txl, &addr, ts).await;
                });
                guard.insert(addr, Some((task, ts)));
@@ -1402,6 +1433,34 @@ impl BluetoothMedia {
        }
        true
    }

    // Per MPS v1.0 (Multi-Profile Specification), disconnecting or failing to connect
    // a profile should not affect the others.
    // Allow partial profiles connection during qualification (phone operations are enabled).
    fn is_complete_profiles_required(&self) -> bool {
        !self.phone_ops_enabled
    }

    // Force the media enters the FullyConnected state and then triggers a retry.
    // This function is only used for qualification as a replacement of normal retry.
    // Usually PTS initiates the connection of the necessary profiles, and Floss should notify
    // CRAS of the new audio device regardless of the unconnected profiles.
    // Still retry in the end because some test cases require that.
    fn force_enter_connected(&mut self, address: String) {
        let addr = match RawAddress::from_string(address.clone()) {
            None => {
                warn!("Invalid device address for force_enter_connected");
                return;
            }
            Some(addr) => addr,
        };
        self.device_states
            .lock()
            .unwrap()
            .insert(addr.clone(), DeviceConnectionStates::FullyConnected);
        self.notify_media_capability_updated(addr);
        self.connect(address);
    }
}

fn get_a2dp_dispatcher(tx: Sender<Message>) -> A2dpCallbacksDispatcher {
@@ -1777,6 +1836,14 @@ impl IBluetoothMedia for BluetoothMedia {
    }

    fn set_active_device(&mut self, address: String) {
        // During MPS tests, there might be some A2DP stream manipulation unexpected to CRAS.
        // CRAS would then attempt to reset the active device. Ignore it during test.
        // TODO(b/265988575): CRAS is migrating to use ResetActiveDevice instead. Remove this
        // after the migration is done.
        if !self.is_complete_profiles_required() && address == String::from("00:00:00:00:00:00") {
            return;
        }

        let addr = match RawAddress::from_string(address.clone()) {
            None => {
                warn!("Invalid device address for set_active_device");
@@ -1799,6 +1866,12 @@ impl IBluetoothMedia for BluetoothMedia {
    }

    fn reset_active_device(&mut self) {
        // During MPS tests, there might be some A2DP stream manipulation unexpected to CRAS.
        // CRAS would then attempt to reset the active device. Ignore it during test.
        if !self.is_complete_profiles_required() {
            return;
        }

        if let Some(a2dp) = self.a2dp.as_mut() {
            a2dp.set_active_device(RawAddress::empty());
        } else {