blob: a624b7581de011e5a9a0553a42bcdfb75fadf16f [file] [log] [blame]
// Copyright 2018 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.
use account_common::{AccountManagerError, LocalAccountId};
use failure::{format_err, ResultExt};
use fidl_fuchsia_auth_account::Status;
use fidl_fuchsia_auth_account_internal::{AccountHandlerControlMarker, AccountHandlerControlProxy};
use fuchsia_app::client::{App, Launcher};
use futures::prelude::*;
use log::{info, warn};
/// The information necessary to maintain a connection to an AccountHandler component instance.
pub struct AccountHandlerConnection {
/// An `App` object for the launched AccountHandler.
///
/// Note: This must remain in scope for the component to remain running, but never needs to be
/// read.
_app: App,
/// A `Proxy` connected to the AccountHandlerControl interface on the launched AccountHandler.
proxy: AccountHandlerControlProxy,
}
/// The url used to launch new AccountHandler component instances.
const ACCOUNT_HANDLER_URL: &str =
"fuchsia-pkg://fuchsia.com/account_handler#meta/account_handler.cmx";
impl AccountHandlerConnection {
/// Launches a new AccountHandler component instance and establishes a connection to its
/// control channel.
pub fn new() -> Result<Self, AccountManagerError> {
info!("Launching new AccountHandler instance");
let launcher = Launcher::new()
.context("Failed to start launcher")
.map_err(|err| AccountManagerError::new(Status::IoError).with_cause(err))?;
let app = launcher
.launch(ACCOUNT_HANDLER_URL.to_string(), None)
.context("Failed to launch AccountHandler")
.map_err(|err| AccountManagerError::new(Status::IoError).with_cause(err))?;
let proxy = app
.connect_to_service(AccountHandlerControlMarker)
.context("Failed to connect to AccountHandlerControl")
.map_err(|err| AccountManagerError::new(Status::InternalError).with_cause(err))?;
Ok(AccountHandlerConnection { _app: app, proxy })
}
/// Launches a new AccountHandler component instance, establishes a connection to its control
/// channel, and requests that it loads an existing account.
pub async fn new_for_account(account_id: &LocalAccountId) -> Result<Self, AccountManagerError> {
let connection = Self::new()?;
let mut fidl_account_id = account_id.clone().into();
match await!(connection.proxy.load_account(&mut fidl_account_id))
.map_err(|err| AccountManagerError::new(Status::IoError).with_cause(err))?
{
Status::Ok => Ok(connection),
stat => Err(AccountManagerError::new(stat)
.with_cause(format_err!("Error loading existing account"))),
}
}
/// Returns the AccountHandlerControlProxy for this connection
pub fn proxy(&self) -> &AccountHandlerControlProxy {
&self.proxy
}
/// Requests that the AccountHandler component instance terminate gracefully.
///
/// Any subsequent operations that attempt to use `proxy()` will fail after this call. The
/// resources associated with the connection when only be freed once the
/// `AccountHandlerConnection` is dropped.
pub async fn terminate(&self) {
let mut event_stream = self.proxy.take_event_stream();
if let Err(err) = self.proxy.terminate() {
warn!("Error gracefully terminating account handler {:?}", err);
} else {
while let Ok(Some(_)) = await!(event_stream.try_next()) {}
info!("Gracefully terminated AccountHandler instance");
}
}
}