1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![recursion_limit = "1024"]

use {
    anyhow::{format_err, Error},
    battery_client::BatteryClient,
    fuchsia_component::server::ServiceFs,
    fuchsia_inspect_derive::Inspect,
    futures::{channel::mpsc, future, pin_mut},
    tracing::{debug, error, info, warn},
};

use crate::{
    audio::AudioControl, config::AudioGatewayFeatureSupport, fidl_service::run_services, hfp::Hfp,
    profile::register_audio_gateway,
};

mod a2dp;
mod audio;
mod config;
mod error;
mod features;
mod fidl_service;
mod hfp;
mod inspect;
mod peer;
mod profile;
mod sco_connector;
mod service_definitions;

#[fuchsia::main(logging_tags = ["hfp-ag"])]
async fn main() -> Result<(), Error> {
    let mut fs = ServiceFs::new();

    let inspector = fuchsia_inspect::Inspector::default();
    if let Err(e) = inspect_runtime::serve(&inspector, &mut fs) {
        warn!("Couldn't serve inspect: {}", e);
    }

    let config = hfp_profile_config::Config::take_from_startup_handle();
    let in_band_sco = config.in_band_sco;
    let feature_support = AudioGatewayFeatureSupport::load_from_config(config);
    debug!(?feature_support, "Starting HFP Audio Gateway");
    let (profile_client, profile_svc) = register_audio_gateway(feature_support)?;

    // Power integration is optional - the HFP component will function even power integration is
    // unavailable.
    let battery_client = match BatteryClient::create() {
        Err(e) => {
            info!("Power integration unavailable: {:?}", e);
            None
        }
        Ok(batt) => Some(batt),
    };

    let (call_manager_sender, call_manager_receiver) = mpsc::channel(1);
    let (test_request_sender, test_request_receiver) = mpsc::channel(1);

    let audio: Box<dyn AudioControl> = if in_band_sco {
        match audio::DaiAudioControl::discover().await {
            Err(e) => {
                error!(?e, "Couldn't setup DAI audio");
                return Err(format_err!("DAI failed"));
            }
            Ok(audio) => Box::new(audio),
        }
    } else {
        match audio::InbandAudioControl::create() {
            Err(e) => {
                error!(?e, "Couldn't setup inband audio");
                return Err(format_err!("Inband failed: {e:?}"));
            }
            Ok(audio) => Box::new(audio),
        }
    };

    let mut hfp = Hfp::new(
        profile_client,
        profile_svc,
        battery_client,
        audio,
        call_manager_receiver,
        feature_support,
        in_band_sco,
        test_request_receiver,
    );
    if let Err(e) = hfp.iattach(&inspector.root(), "hfp") {
        warn!("Couldn't attach inspect to HFP: {:?}", e);
    }

    let hfp = hfp.run();
    pin_mut!(hfp);

    let services = run_services(fs, call_manager_sender, test_request_sender);
    pin_mut!(services);

    info!("HFP Audio Gateway running.");

    match future::select(services, hfp).await {
        future::Either::Left((Ok(()), _)) => {
            warn!("Service FS directory handle closed. Exiting.");
        }
        future::Either::Left((Err(e), _)) => {
            error!("Error encountered running Service FS: {}. Exiting", e);
        }
        future::Either::Right((Ok(()), _)) => {
            warn!("All HFP related connections to this component have been disconnected. Exiting.");
        }
        future::Either::Right((Err(e), _)) => {
            error!("Error encountered running main HFP loop: {}. Exiting.", e);
        }
    }

    Ok(())
}