[auth] Attach AccountManager to AccountHandler

AUTH-118

Tested: CQ, unit and integration tests included.
Change-Id: I2198a75ac44d66c6412cc4963562a1470801d16b
diff --git a/bin/auth/rust/account_common/src/deferred.rs b/bin/auth/rust/account_common/src/deferred.rs
deleted file mode 100644
index 89eacde..0000000
--- a/bin/auth/rust/account_common/src/deferred.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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.
-
-/// A simple wrapper to set an optional value after instantiation.
-///
-/// The value cannot be unset once set. When wrapped in a synchronization primitive, this type is
-/// helpful as an input for asynchronous functions, letting them lazily create shared state on
-/// first use.
-pub struct DeferredOption<T> {
-    value: Option<T>,
-}
-
-impl<T> DeferredOption<T> {
-    /// Constructs an empty `DeferredOption`.
-    pub fn new() -> DeferredOption<T> {
-        DeferredOption { value: None }
-    }
-
-    /// Returns the current value.
-    pub fn get(&self) -> Option<&T> {
-        self.value.as_ref()
-    }
-
-    /// Sets or overrides the current value with `value`.
-    pub fn set(&mut self, value: T) {
-        self.value = Some(value);
-    }
-
-    /// Returns the current value if set, or sets and returns if using `setter` otherwise.
-    pub fn get_or_create<'a, F>(&'a mut self, setter: F) -> &'a mut T
-        where F: FnOnce() -> T
-    {
-        if self.value.is_none() {
-            self.value = Some(setter());
-        }
-        self.value.as_mut().unwrap()
-    }
-}
diff --git a/bin/auth/rust/account_common/src/error.rs b/bin/auth/rust/account_common/src/error.rs
index 0c66273..41b0227 100644
--- a/bin/auth/rust/account_common/src/error.rs
+++ b/bin/auth/rust/account_common/src/error.rs
@@ -35,7 +35,6 @@
     }
 
     /// Sets a cause on the current error.
-    #[allow(dead_code)] // TODO(jsankey): Use this method in the next CL.
     pub fn with_cause<T: Into<Error>>(mut self, cause: T) -> Self {
         self.cause = Some(cause.into());
         self
diff --git a/bin/auth/rust/account_common/src/identifiers.rs b/bin/auth/rust/account_common/src/identifiers.rs
index c8d1925..91b178f 100644
--- a/bin/auth/rust/account_common/src/identifiers.rs
+++ b/bin/auth/rust/account_common/src/identifiers.rs
@@ -12,6 +12,7 @@
 // to be more ergonomic and remove the need for this module.
 
 use std::cmp::Ordering;
+use std::hash::{Hash, Hasher};
 use std::fmt::{Debug, Formatter};
 
 /// Implements `$name` as a new wrapper type.
@@ -20,7 +21,7 @@
 /// in both directions to a type with the same name in `$fidl_crate`. This type must by a struct
 /// contain a single field of type `$type` named 'id'.
 ///
-/// `$type` must implement the following traits: `Debug`, `Clone`, `Eq`, and `Ord`.
+/// `$type` must implement the following traits: `Debug`, `Clone`, `Hash`, `Eq`, and `Ord`.
 ///
 /// # Examples
 ///
@@ -53,6 +54,12 @@
             }
         }
 
+        impl Hash for $name {
+            fn hash<H: Hasher>(&self, state: &mut H) {
+                self.inner.id.hash(state);
+            }
+        }
+
         impl Ord for $name {
             fn cmp(&self, other: &$name) -> Ordering {
                 self.inner.id.cmp(&other.inner.id)
diff --git a/bin/auth/rust/account_common/src/lib.rs b/bin/auth/rust/account_common/src/lib.rs
index 1b5e13d..007f6f3 100644
--- a/bin/auth/rust/account_common/src/lib.rs
+++ b/bin/auth/rust/account_common/src/lib.rs
@@ -10,10 +10,7 @@
 mod error;
 /// More ergonomic wrapper types for FIDL account and persona identifiers.
 mod identifiers;
-/// A simple wrapper to set values after instantiation.
-mod deferred;
 
 pub use crate::error::AccountManagerError;
 pub use crate::identifiers::{FidlGlobalAccountId, FidlLocalAccountId, FidlLocalPersonaId,
                              GlobalAccountId, LocalAccountId, LocalPersonaId};
-pub use crate::deferred::DeferredOption;
diff --git a/bin/auth/rust/account_manager/BUILD.gn b/bin/auth/rust/account_manager/BUILD.gn
index 80bb87d..abf0f25 100644
--- a/bin/auth/rust/account_manager/BUILD.gn
+++ b/bin/auth/rust/account_manager/BUILD.gn
@@ -13,6 +13,7 @@
     "//garnet/bin/auth/rust/account_common",
     "//garnet/public/fidl/fuchsia.auth:fuchsia.auth-rustc",
     "//garnet/public/fidl/fuchsia.auth.account:fuchsia.auth.account-rustc",
+    "//garnet/lib/auth/fidl:account-rustc",
     "//garnet/public/lib/fidl/rust/fidl",
     "//garnet/public/rust/fuchsia-app",
     "//garnet/public/rust/fuchsia-async",
diff --git a/bin/auth/rust/account_manager/src/account_handler_connection.rs b/bin/auth/rust/account_manager/src/account_handler_connection.rs
new file mode 100644
index 0000000..a624b75
--- /dev/null
+++ b/bin/auth/rust/account_manager/src/account_handler_connection.rs
@@ -0,0 +1,82 @@
+// 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");
+        }
+    }
+}
diff --git a/bin/auth/rust/account_manager/src/account_manager.rs b/bin/auth/rust/account_manager/src/account_manager.rs
index 622d37c..bbb1b24 100644
--- a/bin/auth/rust/account_manager/src/account_manager.rs
+++ b/bin/auth/rust/account_manager/src/account_manager.rs
@@ -2,19 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use account_common::{FidlLocalAccountId, LocalAccountId};
+use account_common::{AccountManagerError, FidlLocalAccountId, LocalAccountId};
 use fidl::encoding::OutOfLine;
 use fidl::endpoints::{ClientEnd, ServerEnd};
-use fidl::Error;
 use fidl_fuchsia_auth::{AuthState, AuthStateSummary, AuthenticationContextProviderMarker};
 use fidl_fuchsia_auth_account::{
     AccountAuthState, AccountListenerMarker, AccountListenerOptions, AccountManagerRequest,
     AccountManagerRequestStream, AccountMarker, Status,
 };
+
+use failure::Error;
+use futures::lock::Mutex;
 use futures::prelude::*;
-use log::info;
-use parking_lot::Mutex;
-use std::collections::BTreeSet;
+use log::{info, warn};
+use std::collections::BTreeMap;
+use std::sync::Arc;
+
+use crate::account_handler_connection::AccountHandlerConnection;
 
 /// The core component of the account system for Fuchsia.
 ///
@@ -23,12 +27,10 @@
 /// service providers, and launches and delegates to AccountHandler component instances to
 /// determine the detailed state and authentication for each account.
 pub struct AccountManager {
-    /// (Temporary) The next unused local account identifier.
-    // TODO(jsankey): Replace this temporary sequential ID assignment with randomness.
-    next_id: Mutex<u64>,
-
-    /// An ordered set of LocalAccountIds for accounts provisioned on the current device.
-    ids: Mutex<BTreeSet<LocalAccountId>>,
+    /// An ordered map from the `LocalAccountId` of all accounts on the device to an
+    /// `Option` containing the `AcountHandlerConnection` used to communicate with the associated
+    /// AccountHandler if a connecton exists, or None otherwise.
+    ids_to_handlers: Mutex<BTreeMap<LocalAccountId, Option<Arc<AccountHandlerConnection>>>>,
 }
 
 impl AccountManager {
@@ -41,8 +43,7 @@
     /// Constructs a new AccountManager with no accounts.
     pub fn new() -> AccountManager {
         AccountManager {
-            next_id: Mutex::new(1),
-            ids: Mutex::new(BTreeSet::new()),
+            ids_to_handlers: Mutex::new(BTreeMap::new()),
         }
     }
 
@@ -51,19 +52,19 @@
         &self, mut stream: AccountManagerRequestStream,
     ) -> Result<(), Error> {
         while let Some(req) = await!(stream.try_next())? {
-            self.handle_request(req)?;
+            await!(self.handle_request(req))?;
         }
         Ok(())
     }
 
     /// Handles a single request to the AccountManager.
-    pub fn handle_request(&self, req: AccountManagerRequest) -> Result<(), Error> {
+    pub async fn handle_request(&self, req: AccountManagerRequest) -> Result<(), fidl::Error> {
         match req {
             AccountManagerRequest::GetAccountIds { responder } => {
-                responder.send(&mut self.get_account_ids().iter_mut())
+                responder.send(&mut await!(self.get_account_ids()).iter_mut())
             }
             AccountManagerRequest::GetAccountAuthStates { responder } => {
-                let mut response = self.get_account_auth_states();
+                let mut response = await!(self.get_account_auth_states());
                 responder.send(response.0, &mut response.1.iter_mut())
             }
             AccountManagerRequest::GetAccount {
@@ -71,14 +72,18 @@
                 auth_context_provider,
                 account,
                 responder,
-            } => responder.send(self.get_account(id.into(), auth_context_provider, account)),
+            } => responder.send(await!(self.get_account(
+                id.into(),
+                auth_context_provider,
+                account
+            ))),
             AccountManagerRequest::RegisterAccountListener {
                 listener,
                 options,
                 responder,
             } => responder.send(self.register_account_listener(listener, options)),
             AccountManagerRequest::RemoveAccount { id, responder } => {
-                responder.send(self.remove_account(id.into()))
+                responder.send(await!(self.remove_account(id.into())))
             }
             AccountManagerRequest::ProvisionFromAuthProvider {
                 auth_context_provider,
@@ -90,24 +95,49 @@
                 responder.send(response.0, response.1.as_mut().map(OutOfLine))
             }
             AccountManagerRequest::ProvisionNewAccount { responder } => {
-                let mut response = self.provision_new_account();
+                let mut response = await!(self.provision_new_account());
                 responder.send(response.0, response.1.as_mut().map(OutOfLine))
             }
         }
     }
 
-    fn get_account_ids(&self) -> Vec<FidlLocalAccountId> {
-        self.ids.lock().iter().map(|id| id.clone().into()).collect()
+    /// Returns an `AccountHandlerConnection` for the specified `LocalAccountId`, either by
+    /// returning the existing entry from the map or by creating and adding a new entry to the map.
+    async fn get_handler_for_existing_account<'a>(
+        &'a self, account_id: &'a LocalAccountId,
+    ) -> Result<Arc<AccountHandlerConnection>, AccountManagerError> {
+        let mut ids_to_handlers_lock = await!(self.ids_to_handlers.lock());
+        match ids_to_handlers_lock.get(account_id) {
+            None => return Err(AccountManagerError::new(Status::NotFound)),
+            Some(Some(existing_handler)) => return Ok(Arc::clone(existing_handler)),
+            Some(None) => { /* ID is valid but a handler doesn't exist yet */ }
+        }
+
+        // Initialize a new account handler if not.
+        let new_handler = Arc::new(await!(AccountHandlerConnection::new_for_account(
+            account_id
+        ))?);
+        ids_to_handlers_lock.insert(account_id.clone(), Some(Arc::clone(&new_handler)));
+        Ok(new_handler)
     }
 
-    fn get_account_auth_states(&self) -> (Status, Vec<AccountAuthState>) {
-        // TODO(jsankey): Collect authentication state from AccountHandler instances
-        // rather than returning a fixed value.
+    async fn get_account_ids(&self) -> Vec<FidlLocalAccountId> {
+        await!(self.ids_to_handlers.lock())
+            .keys()
+            .map(|id| id.clone().into())
+            .collect()
+    }
+
+    async fn get_account_auth_states(&self) -> (Status, Vec<AccountAuthState>) {
+        // TODO(jsankey): Collect authentication state from AccountHandler instances rather than
+        // returning a fixed value. This will involve opening account handler connections (in
+        // parallel) for all of the accounts where encryption keys for the account's data partition
+        // are available.
+        let ids_to_handlers_lock = await!(self.ids_to_handlers.lock());
         (
             Status::Ok,
-            self.ids
-                .lock()
-                .iter()
+            ids_to_handlers_lock
+                .keys()
                 .map(|id| AccountAuthState {
                     account_id: id.clone().into(),
                     auth_state: Self::DEFAULT_AUTH_STATE,
@@ -116,13 +146,26 @@
         )
     }
 
-    fn get_account(
-        &self, _id: LocalAccountId,
-        _auth_context_provider: ClientEnd<AuthenticationContextProviderMarker>,
-        _account: ServerEnd<AccountMarker>,
+    async fn get_account(
+        &self, id: LocalAccountId,
+        auth_context_provider: ClientEnd<AuthenticationContextProviderMarker>,
+        account: ServerEnd<AccountMarker>,
     ) -> Status {
-        // TODO(jsankey): Implement this method
-        Status::InternalError
+        let account_handler = match await!(self.get_handler_for_existing_account(&id)) {
+            Ok(account_handler) => account_handler,
+            Err(err) => {
+                warn!("Failure getting account handler connection: {:?}", err);
+                return err.status;
+            }
+        };
+
+        await!(account_handler
+            .proxy()
+            .get_account(auth_context_provider, account))
+        .unwrap_or_else(|err| {
+            warn!("Failure calling get account: {:?}", err);
+            Status::IoError
+        })
     }
 
     fn register_account_listener(
@@ -132,26 +175,61 @@
         Status::InternalError
     }
 
-    fn remove_account(&self, id: LocalAccountId) -> Status {
-        let mut ids_lock = self.ids.lock();
-        if ids_lock.contains(&id) {
-            ids_lock.remove(&id);
-            info!("Removing account {:?}", id);
-            // TODO(jsankey): Persist the change in installed accounts.
-            Status::Ok
-        } else {
-            Status::NotFound
+    async fn remove_account(&self, id: LocalAccountId) -> Status {
+        // TODO(jsankey): Open an account handler if necessary and ask it to remove persistent
+        // storage for the account.
+        match await!(self.ids_to_handlers.lock()).remove(&id) {
+            None => return Status::NotFound,
+            Some(None) => info!("Removing account without open handler: {:?}", id),
+            Some(Some(account_handler)) => {
+                info!("Removing account and terminating its handler: {:?}", id);
+                await!(account_handler.terminate());
+            }
         }
+        Status::Ok
     }
 
-    fn provision_new_account(&self) -> (Status, Option<FidlLocalAccountId>) {
-        let mut next_id_lock = self.next_id.lock();
-        let new_id = LocalAccountId::new(*next_id_lock);
-        self.ids.lock().insert(new_id.clone());
-        *next_id_lock += 1;
-        info!("Adding new local account {:?}", new_id);
-        // TODO(jsankey): Persist the change in installed accounts.
-        (Status::Ok, Some(new_id.into()))
+    async fn provision_new_account(&self) -> (Status, Option<FidlLocalAccountId>) {
+        let account_handler = match AccountHandlerConnection::new() {
+            Ok(connection) => Arc::new(connection),
+            Err(err) => {
+                warn!("Failure creating account handler connection: {:?}", err);
+                return (err.status, None);
+            }
+        };
+
+        let account_id = match await!(account_handler.proxy().create_account()) {
+            Ok((Status::Ok, Some(account_id))) => LocalAccountId::from(*account_id),
+            Ok((Status::Ok, None)) => {
+                warn!("Failure creating account, handler returned success without ID");
+                return (Status::InternalError, None);
+            }
+            Ok((stat, _)) => {
+                warn!("Failure creating new account, handler returned {:?}", stat);
+                return (stat, None);
+            }
+            Err(err) => {
+                warn!("Failure calling create account: {:?}", err);
+                return (Status::IoError, None);
+            }
+        };
+
+        info!("Adding new local account {:?}", account_id);
+        // TODO(jsankey): Persist the change in installed accounts, ensuring this has succeeded
+        // before adding to our in-memory state.
+
+        let mut ids_to_handlers_lock = await!(self.ids_to_handlers.lock());
+        if ids_to_handlers_lock.get(&account_id).is_some() {
+            // IDs are 64 bit integers that are meant to be random. Its very unlikely we'll create
+            // the same one twice but not impossible.
+            // TODO(jsankey): Once account handler is handling persistent state it may be able to
+            // detect this condition itself, if not it needs to be told to delete any state it has
+            // created for this user we're not going to add.
+            warn!("Duplicate ID creating new account");
+            return (Status::UnknownError, None);
+        }
+        ids_to_handlers_lock.insert(account_id.clone(), Some(account_handler));
+        (Status::Ok, Some(account_id.into()))
     }
 
     fn provision_from_auth_provider(
@@ -195,10 +273,14 @@
             .expect("Executor run failed.")
     }
 
-    fn create_test_object(next_id: u64, existing_ids: Vec<u64>) -> AccountManager {
+    fn create_test_object(existing_ids: Vec<u64>) -> AccountManager {
         AccountManager {
-            next_id: Mutex::new(next_id),
-            ids: Mutex::new(existing_ids.into_iter().map(LocalAccountId::new).collect()),
+            ids_to_handlers: Mutex::new(
+                existing_ids
+                    .into_iter()
+                    .map(|id| (LocalAccountId::new(id), None))
+                    .collect(),
+            ),
         }
     }
 
@@ -208,9 +290,10 @@
             .collect()
     }
 
-    fn fidl_local_id_box(i: u64) -> Box<FidlLocalAccountId> {
-        Box::new(FidlLocalAccountId { id: i })
-    }
+    /// Note: Many AccountManager methods launch instances of an AccountHandler. Since its
+    /// currently not convenient to mock out this component launching in Rust, we rely on the
+    /// hermertic component test to provide coverage for these areas and only cover the in-process
+    /// behavior with this unit-test.
 
     #[test]
     fn test_initially_empty() {
@@ -225,44 +308,10 @@
     }
 
     #[test]
-    fn test_provision_new_account() {
-        request_stream_test(AccountManager::new(), async move |proxy| {
-            // Add two accounts.
-            assert_eq!(
-                await!(proxy.provision_new_account())?,
-                (Status::Ok, Some(fidl_local_id_box(1)))
-            );
-            assert_eq!(
-                await!(proxy.provision_new_account())?,
-                (Status::Ok, Some(fidl_local_id_box(2)))
-            );
-
-            // Verify both are visible in the ID and AuthState getters.
-            assert_eq!(
-                await!(proxy.get_account_ids())?,
-                fidl_local_id_vec(vec![1, 2])
-            );
-
-            let expected_account_auth_states = vec![1, 2]
-                .into_iter()
-                .map(|id| AccountAuthState {
-                    account_id: FidlLocalAccountId { id },
-                    auth_state: AccountManager::DEFAULT_AUTH_STATE,
-                })
-                .collect();
-            assert_eq!(
-                await!(proxy.get_account_auth_states())?,
-                (Status::Ok, expected_account_auth_states)
-            );
-            Ok(())
-        });
-    }
-
-    #[test]
     fn test_remove_missing_account() {
         request_stream_test(
             // Manually create an account manager with one account.
-            create_test_object(2, vec![1]),
+            create_test_object(vec![1]),
             async move |proxy| {
                 // Try to delete a very different account from the one we added.
                 assert_eq!(
@@ -278,7 +327,7 @@
     fn test_remove_present_account() {
         request_stream_test(
             // Manually create an account manager with two accounts.
-            create_test_object(3, vec![1, 2]),
+            create_test_object(vec![1, 2]),
             async move |proxy| {
                 // Try to remove the first one.
                 assert_eq!(
diff --git a/bin/auth/rust/account_manager/src/main.rs b/bin/auth/rust/account_manager/src/main.rs
index 942a90f..188b814 100644
--- a/bin/auth/rust/account_manager/src/main.rs
+++ b/bin/auth/rust/account_manager/src/main.rs
@@ -14,6 +14,7 @@
 #![deny(missing_docs)]
 #![feature(async_await, await_macro, futures_api)]
 
+mod account_handler_connection;
 mod account_manager;
 
 use crate::account_manager::AccountManager;
diff --git a/bin/auth/rust/meta/account_handler.cmx b/bin/auth/rust/meta/account_handler.cmx
index 131cc19..ed91ce2 100644
--- a/bin/auth/rust/meta/account_handler.cmx
+++ b/bin/auth/rust/meta/account_handler.cmx
@@ -3,6 +3,9 @@
         "binary": "bin/app"
     },
     "sandbox": {
-        "features": [ "persistent-storage" ]
+        "features": [ "persistent-storage" ],
+        "services": [
+            "fuchsia.logger.LogSink"
+        ]
     }
 }
diff --git a/bin/auth/rust/meta/account_manager.cmx b/bin/auth/rust/meta/account_manager.cmx
index f0c9d6b..8465166 100644
--- a/bin/auth/rust/meta/account_manager.cmx
+++ b/bin/auth/rust/meta/account_manager.cmx
@@ -6,6 +6,7 @@
         "features": [ "persistent-storage" ],
         "services": [
             "fuchsia.logger.LogSink",
+            "fuchsia.sys.Launcher",
             "fuchsia.tracelink.Registry"
         ]
     }
diff --git a/bin/auth/testing/account_manager_integration/tests/account.rs b/bin/auth/testing/account_manager_integration/tests/account.rs
index 8b97509..c710544 100644
--- a/bin/auth/testing/account_manager_integration/tests/account.rs
+++ b/bin/auth/testing/account_manager_integration/tests/account.rs
@@ -3,10 +3,16 @@
 // found in the LICENSE file.
 
 use failure::{format_err, Error};
-use fidl_fuchsia_auth_account::{AccountManagerMarker, AccountManagerProxy, Status};
+use fidl::endpoints::{ClientEnd, ServerEnd};
+use fidl_fuchsia_auth::AuthStateSummary;
+use fidl_fuchsia_auth_account::{
+    AccountManagerMarker, AccountManagerProxy, AccountProxy, LocalAccountId, Status,
+};
 use fuchsia_async as fasync;
+use fuchsia_zircon as zx;
 use futures::prelude::*;
 
+/// Executes the supplied test function against a connected AccountManagerProxy.
 fn proxy_test<TestFn, Fut>(test_fn: TestFn)
 where
     TestFn: FnOnce(AccountManagerProxy) -> Fut,
@@ -21,24 +27,84 @@
         .expect("Executor run failed.")
 }
 
-#[test]
-fn test_provision_new_account() {
-    proxy_test(async move |account_manager| {
-        assert_eq!(await!(account_manager.get_account_ids())?, vec![]);
-        match await!(account_manager.provision_new_account())? {
-            (Status::Ok, Some(new_account_id)) => {
-                assert_eq!(
-                    await!(account_manager.get_account_ids())?,
-                    vec![*new_account_id]
-                );
-                Ok(())
-            }
-            (status, _) => Err(format_err!(
-                "ProvisionNewAccount returned status: {:?}",
-                status
-            )),
-        }
-    });
+/// Calls provision_new_account on the supplied account_manager, returning an error on any
+/// non-OK responses, or the account ID on success.
+async fn provision_new_account(
+    account_manager: &AccountManagerProxy,
+) -> Result<LocalAccountId, Error> {
+    match await!(account_manager.provision_new_account())? {
+        (Status::Ok, Some(new_account_id)) => Ok(*new_account_id),
+        (status, _) => Err(format_err!(
+            "ProvisionNewAccount returned status: {:?}",
+            status
+        )),
+    }
 }
 
-// TODO(jsankey): Add additional tests as AccountManager and AccountHandler are connected.
+// TODO(jsankey): Work with ComponentFramework and cramertj@ to develop a nice Rust equivalent of
+// the C++ TestWithEnvironment fixture to provide isolated environments for each test case. For now
+// we verify all functionality in a single test case.
+#[test]
+fn test_account_functionality() {
+    proxy_test(async move |account_manager| {
+        // Verify we initially have no accounts.
+        assert_eq!(await!(account_manager.get_account_ids())?, vec![]);
+
+        // Provision a new account.
+        let mut account_1 = await!(provision_new_account(&account_manager))?;
+        assert_eq!(
+            await!(account_manager.get_account_ids())?,
+            vec![LocalAccountId { id: account_1.id }]
+        );
+
+        // Provision a second new account and verify it has a different ID.
+        let mut account_2 = await!(provision_new_account(&account_manager))?;
+        assert_ne!(account_1.id, account_2.id);
+
+        // Connect a channel to one of these accounts and verify it's usable.
+        let (_, acp_client_chan) = zx::Channel::create()?;
+        let (account_server_chan, account_client_chan) = zx::Channel::create()?;
+        assert_eq!(
+            await!(account_manager.get_account(
+                &mut account_1,
+                ClientEnd::new(acp_client_chan),
+                ServerEnd::new(account_server_chan)
+            ))?,
+            Status::Ok
+        );
+        let account =
+            AccountProxy::new(fasync::Channel::from_channel(account_client_chan).unwrap());
+        let account_auth_state = match await!(account.get_auth_state())? {
+            (Status::Ok, Some(auth_state)) => *auth_state,
+            (status, _) => return Err(format_err!("GetAuthState returned status: {:?}", status)),
+        };
+        assert_eq!(account_auth_state.summary, AuthStateSummary::Unknown);
+
+        // Connect a channel to the account's default persona and verify it's usable.
+        let (persona_server_chan, persona_client_chan) = zx::Channel::create()?;
+        assert_eq!(
+            await!(account.get_default_persona(ServerEnd::new(persona_server_chan)))?.0,
+            Status::Ok
+        );
+        let persona =
+            AccountProxy::new(fasync::Channel::from_channel(persona_client_chan).unwrap());
+        let persona_auth_state = match await!(persona.get_auth_state())? {
+            (Status::Ok, Some(auth_state)) => *auth_state,
+            (status, _) => return Err(format_err!("GetAuthState returned status: {:?}", status)),
+        };
+        assert_eq!(persona_auth_state.summary, AuthStateSummary::Unknown);
+
+        // Delete both accounts and verify they are removed.
+        assert_eq!(
+            await!(account_manager.remove_account(&mut account_1))?,
+            Status::Ok
+        );
+        assert_eq!(
+            await!(account_manager.remove_account(&mut account_2))?,
+            Status::Ok
+        );
+        assert_eq!(await!(account_manager.get_account_ids())?, vec![]);
+
+        Ok(())
+    });
+}