[Nearby] Define NearbyProcessManager singleton
This class manages the lifetime and profile affinity of the sandbox
process that runs Nearby Connections. Only one instance of the process
will run at a time and it will be associated with one profile only. A
profile can be set as the active profile (usually via an explicit user
action) after which it gets access to the mojo interfaces running in the
sandboxed process.
Bug: 1084576
Change-Id: I50c8c61e1b7b85a078f4fdea96e0ecda1ac57123
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2196486
Reviewed-by: Dominic Battré <[email protected]>
Reviewed-by: Alex Chau <[email protected]>
Commit-Queue: Richard Knoll <[email protected]>
Cr-Commit-Position: refs/heads/master@{#772209}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 07b4aee..ccd164b 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3411,6 +3411,8 @@
"nearby_sharing/nearby_connection.h",
"nearby_sharing/nearby_connections_manager.h",
"nearby_sharing/nearby_constants.h",
+ "nearby_sharing/nearby_process_manager.cc",
+ "nearby_sharing/nearby_process_manager.h",
"nearby_sharing/nearby_sharing_prefs.cc",
"nearby_sharing/nearby_sharing_prefs.h",
"nearby_sharing/nearby_sharing_service.h",
diff --git a/chrome/browser/nearby_sharing/nearby_process_manager.cc b/chrome/browser/nearby_sharing/nearby_process_manager.cc
new file mode 100644
index 0000000..d8b749d
--- /dev/null
+++ b/chrome/browser/nearby_sharing/nearby_process_manager.cc
@@ -0,0 +1,220 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/nearby_sharing/nearby_process_manager.h"
+
+#include "base/files/file_path.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/nearby_sharing/nearby_sharing_prefs.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/sharing/webrtc/sharing_mojo_service.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/service_process_host.h"
+
+namespace {
+
+ProfileAttributesEntry* GetStoredNearbyProfile() {
+ PrefService* local_state = g_browser_process->local_state();
+ if (!local_state)
+ return nullptr;
+
+ base::FilePath advertising_profile_path =
+ local_state->GetFilePath(kNearbySharingActiveProfilePrefName);
+ if (advertising_profile_path.empty())
+ return nullptr;
+
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ if (!profile_manager)
+ return nullptr;
+
+ ProfileAttributesStorage& storage =
+ profile_manager->GetProfileAttributesStorage();
+
+ ProfileAttributesEntry* entry;
+ if (!storage.GetProfileAttributesWithPath(advertising_profile_path, &entry)) {
+ // Stored profile path is invalid so remove it.
+ local_state->ClearPref(kNearbySharingActiveProfilePrefName);
+ return nullptr;
+ }
+ return entry;
+}
+
+void SetStoredNearbyProfile(Profile* profile) {
+ PrefService* local_state = g_browser_process->local_state();
+ if (!local_state)
+ return;
+
+ if (profile) {
+ local_state->SetFilePath(kNearbySharingActiveProfilePrefName,
+ profile->GetPath());
+ } else {
+ local_state->ClearPref(kNearbySharingActiveProfilePrefName);
+ }
+}
+
+bool IsStoredNearbyProfile(Profile* profile) {
+ ProfileAttributesEntry* entry = GetStoredNearbyProfile();
+ if (!entry)
+ return profile == nullptr;
+ return profile && entry->GetPath() == profile->GetPath();
+}
+
+} // namespace
+
+// static
+NearbyProcessManager& NearbyProcessManager::GetInstance() {
+ static base::NoDestructor<NearbyProcessManager> instance;
+ return *instance;
+}
+
+void NearbyProcessManager::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void NearbyProcessManager::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+ProfileAttributesEntry* NearbyProcessManager::GetActiveProfile() const {
+ return GetStoredNearbyProfile();
+}
+
+bool NearbyProcessManager::IsActiveProfile(Profile* profile) const {
+ // If the active profile is not loaded yet, try looking in prefs.
+ // TODO(knollr): Add a test for this.
+ if (!active_profile_)
+ return IsStoredNearbyProfile(profile);
+
+ return active_profile_ == profile;
+}
+
+bool NearbyProcessManager::IsAnyProfileActive() const {
+ return !IsActiveProfile(/*profile=*/nullptr);
+}
+
+void NearbyProcessManager::SetActiveProfile(Profile* profile) {
+ if (IsActiveProfile(profile))
+ return;
+
+ active_profile_ = profile;
+ SetStoredNearbyProfile(active_profile_);
+ StopProcess(active_profile_);
+
+ for (auto& observer : observers_)
+ observer.OnNearbyProfileChanged(profile);
+}
+
+void NearbyProcessManager::ClearActiveProfile() {
+ SetActiveProfile(/*profile=*/nullptr);
+}
+
+location::nearby::connections::mojom::NearbyConnections*
+NearbyProcessManager::GetOrStartNearbyConnections(Profile* profile) {
+ if (!IsActiveProfile(profile))
+ return nullptr;
+
+ // Launch a new Nearby Connections interface if required.
+ if (!connections_.is_bound())
+ BindNearbyConnections();
+
+ return connections_.get();
+}
+
+void NearbyProcessManager::StopProcess(Profile* profile) {
+ if (!IsActiveProfile(profile))
+ return;
+
+ bool was_running = sharing_process_.is_bound();
+
+ connections_host_.reset();
+ connections_.reset();
+ sharing_process_.reset();
+
+ if (was_running) {
+ for (auto& observer : observers_)
+ observer.OnNearbyProcessStopped();
+ }
+}
+
+void NearbyProcessManager::OnProfileAdded(Profile* profile) {
+ // Cache active |profile| once it loads so we don't have to check prefs.
+ if (IsActiveProfile(profile))
+ active_profile_ = profile;
+}
+
+void NearbyProcessManager::OnProfileMarkedForPermanentDeletion(
+ Profile* profile) {
+ if (IsActiveProfile(profile))
+ SetActiveProfile(nullptr);
+}
+
+void NearbyProcessManager::BindSharingProcess(
+ mojo::PendingRemote<sharing::mojom::Sharing> sharing) {
+ sharing_process_.Bind(std::move(sharing));
+ // base::Unretained() is safe as |this| is a singleton.
+ sharing_process_.set_disconnect_handler(base::BindOnce(
+ &NearbyProcessManager::OnNearbyProcessStopped, base::Unretained(this)));
+}
+
+NearbyProcessManager::NearbyProcessManager() {
+ // profile_manager() might be null in tests or during shutdown.
+ if (auto* manager = g_browser_process->profile_manager())
+ manager->AddObserver(this);
+}
+
+NearbyProcessManager::~NearbyProcessManager() {
+ if (auto* manager = g_browser_process->profile_manager())
+ manager->RemoveObserver(this);
+}
+
+void NearbyProcessManager::LaunchNewProcess() {
+ // Stop any running process and mojo pipes.
+ StopProcess(active_profile_);
+
+ // Launch a new sandboxed process.
+ // TODO(knollr): Set process name to "Nearby Sharing".
+ BindSharingProcess(sharing::LaunchSharing());
+}
+
+void NearbyProcessManager::BindNearbyConnections() {
+ // Start a new process if there is none running yet.
+ if (!sharing_process_.is_bound())
+ LaunchNewProcess();
+
+ // Create the Nearby Connections stack in the sandboxed process.
+ // base::Unretained() calls below are safe as |this| is a singleton.
+ sharing_process_->CreateNearbyConnections(
+ connections_host_.BindNewPipeAndPassRemote(),
+ base::BindOnce(&NearbyProcessManager::OnNearbyConnections,
+ base::Unretained(this),
+ connections_.BindNewPipeAndPassReceiver()));
+
+ // Terminate the process if the Nearby Connections interface disconnects as
+ // that indicated an incorrect state and we have to restart the process.
+ connections_host_.set_disconnect_handler(base::BindOnce(
+ &NearbyProcessManager::OnNearbyProcessStopped, base::Unretained(this)));
+ connections_.set_disconnect_handler(base::BindOnce(
+ &NearbyProcessManager::OnNearbyProcessStopped, base::Unretained(this)));
+}
+
+void NearbyProcessManager::OnNearbyConnections(
+ mojo::PendingReceiver<NearbyConnectionsMojom> receiver,
+ mojo::PendingRemote<NearbyConnectionsMojom> remote) {
+ if (!mojo::FusePipes(std::move(receiver), std::move(remote))) {
+ LOG(WARNING) << "Failed to initialize Nearby Connectins process";
+ StopProcess(active_profile_);
+ return;
+ }
+
+ for (auto& observer : observers_)
+ observer.OnNearbyProcessStarted();
+}
+
+void NearbyProcessManager::OnNearbyProcessStopped() {
+ StopProcess(active_profile_);
+}
diff --git a/chrome/browser/nearby_sharing/nearby_process_manager.h b/chrome/browser/nearby_sharing/nearby_process_manager.h
new file mode 100644
index 0000000..5722f3e
--- /dev/null
+++ b/chrome/browser/nearby_sharing/nearby_process_manager.h
@@ -0,0 +1,160 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NEARBY_SHARING_NEARBY_PROCESS_MANAGER_H_
+#define CHROME_BROWSER_NEARBY_SHARING_NEARBY_PROCESS_MANAGER_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/no_destructor.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/profiles/profile_manager_observer.h"
+#include "chrome/services/sharing/public/mojom/nearby_connections.mojom.h"
+#include "chrome/services/sharing/public/mojom/sharing.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+class Profile;
+class ProfileAttributesEntry;
+
+// Manages the lifetime of the Nearby process. It runs the Nearby Connections
+// library and Nearby Sharing data decoding. Only one instance of the process is
+// supported at a time.
+class NearbyProcessManager
+ : public ProfileManagerObserver,
+ public location::nearby::connections::mojom::NearbyConnectionsHost {
+ public:
+ using NearbyConnectionsMojom =
+ location::nearby::connections::mojom::NearbyConnections;
+ using NearbyConnectionsHostMojom =
+ location::nearby::connections::mojom::NearbyConnectionsHost;
+
+ // Returns an instance to the singleton of this class. This is used from
+ // multiple BCKS and only allows the first one to launch a process.
+ static NearbyProcessManager& GetInstance();
+
+ // Observes the global state of the NearbyProcessManager.
+ class Observer : public base::CheckedObserver {
+ public:
+ // Called when the |profile| got set as the active profile.
+ virtual void OnNearbyProfileChanged(Profile* profile) = 0;
+ // Called when the Nearby process has started. This happens after a profile
+ // called one of the GetOrStart*() methods.
+ virtual void OnNearbyProcessStarted() = 0;
+ // Called when the Nearby process has stopped. This can happen when the
+ // process gets stopped to switch to a different profile or when the process
+ // gets killed by the system.
+ virtual void OnNearbyProcessStopped() = 0;
+ };
+
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // Gets the entry for the currently active profile or nullptr if no profile is
+ // set. We return a ProfileAttributesEntry instead of a Profile as the active
+ // profile might not be loaded yet and we do not want to load it here.
+ ProfileAttributesEntry* GetActiveProfile() const;
+
+ // Returns whether the |profile| is the active profile to use the Nearby
+ // process. Convenience method to calling GetActiveProfile() and manually
+ // comparing if they match.
+ bool IsActiveProfile(Profile* profile) const;
+
+ // Returns if any profile is currently set as the active profile. Note that
+ // the active profile might not be loaded yet.
+ bool IsAnyProfileActive() const;
+
+ // Starts an exclusive usage of the Nearby process for the given |profile|.
+ // This will stop the process if it is currently running for a different
+ // profile. After calling this the client may call any of the GetOrStart*()
+ // methods below to start up a new sandboxed process.
+ void SetActiveProfile(Profile* profile);
+
+ // Removes any stored active profile. This will stop the process if it is
+ // currently running for that profile.
+ void ClearActiveProfile();
+
+ // Gets a pointer to the Nearby Connections interface. If there is currently
+ // no process running this will start a new sandboxed process. This will
+ // only work if the |profile| is currently set as the active profile.
+ // Returns a mojo interface to the Nearby Connections library inside the
+ // sandbox if this |profile| is allowed to access it and nullptr otherwise.
+ // Don't store this pointer as it might get invalid if the process gets
+ // stopped (via the OS or StopProcess()). That event can be observed via
+ // Observer::OnNearbyProcessStopped() and a client can decide to restart the
+ // process (e.g. via backoff timer) if it is still the active profile.
+ NearbyConnectionsMojom* GetOrStartNearbyConnections(Profile* profile);
+
+ // TODO(knollr): Add once the mojo interface for the decoder is submitted.
+ // Gets a pointer to the Nearby Decoder interface. Starts a new process if
+ // there is none running already or reuses an existing one. The same
+ // limitations around profiles and lifetime in GetOrStartNearbyConnections()
+ // apply here as well.
+ // NearbySharingDecoderMojom* GetOrStartNearbyDecoder(Profile* profile);
+
+ // Stops the Nearby process if the |profile| is the active profile. This may
+ // be used to save resources or to force stop any communication of the
+ // Nearby Connections library if it should not be used right now. This will
+ // not change the active profile and can be used to temporarily stop the
+ // process (e.g. on screen lock) while keeping the active profile.
+ void StopProcess(Profile* profile);
+
+ // ProfileManagerObserver:
+ void OnProfileAdded(Profile* profile) override;
+ void OnProfileMarkedForPermanentDeletion(Profile* profile) override;
+
+ // Binds the given |sharing| remote to be used as the interface to the Sharing
+ // process running in a sandbox.
+ void BindSharingProcess(mojo::PendingRemote<sharing::mojom::Sharing> sharing);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(NearbyProcessManagerTest, AddRemoveObserver);
+ friend class base::NoDestructor<NearbyProcessManager>;
+
+ // This class is a singleton.
+ NearbyProcessManager();
+ ~NearbyProcessManager() override;
+
+ // Launches a new sandboxed process and stops any currently running one. This
+ // process is then used to run the Nearby Connections library. The process
+ // will use the current profile to initialize Nearby Connections as returned
+ // by UsedProfile().
+ void LaunchNewProcess();
+
+ // Binds a new pipe to the Nearby Connections library. May start a new process
+ // if there is none running yet.
+ void BindNearbyConnections();
+
+ // Called by the sandboxed process after initializing the Nearby Connections
+ // library.
+ void OnNearbyConnections(
+ mojo::PendingReceiver<NearbyConnectionsMojom> receiver,
+ mojo::PendingRemote<NearbyConnectionsMojom> remote);
+
+ // Called if any of the mojo interfaces to the sandboxed process disconnects.
+ // If that happens we stop the process and notify all observers via
+ // Observer::OnNearbyProcessStopped().
+ void OnNearbyProcessStopped();
+
+ // The bound remote to a sandboxed process.
+ mojo::Remote<sharing::mojom::Sharing> sharing_process_;
+ // The bound remote to the Nearby Connections library inside the sandbox.
+ mojo::Remote<NearbyConnectionsMojom> connections_;
+ // TODO(knollr): Add once the mojo interface for the decoder is submitted.
+ // The bound remote to the Nearby Decoder interface inside the sandbox.
+ // mojo::Remote<NearbySharingDecoderMojom> decoder_;
+
+ // Host interface for the Nearby Connections interface.
+ mojo::Receiver<NearbyConnectionsHostMojom> connections_host_{this};
+ // All registered observers, typically one per loaded profile.
+ base::ObserverList<Observer> observers_;
+ // Profile using the Nearby process. This might be nullptr if the active
+ // profile has not been loaded yet.
+ Profile* active_profile_ = nullptr;
+};
+
+#endif // CHROME_BROWSER_NEARBY_SHARING_NEARBY_PROCESS_MANAGER_H_
diff --git a/chrome/browser/nearby_sharing/nearby_process_manager_unittest.cc b/chrome/browser/nearby_sharing/nearby_process_manager_unittest.cc
new file mode 100644
index 0000000..e2f3f6e20
--- /dev/null
+++ b/chrome/browser/nearby_sharing/nearby_process_manager_unittest.cc
@@ -0,0 +1,278 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/nearby_sharing/nearby_process_manager.h"
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/run_loop.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/services/sharing/public/mojom/nearby_connections.mojom.h"
+#include "chrome/services/sharing/public/mojom/nearby_connections_types.mojom.h"
+#include "chrome/services/sharing/public/mojom/sharing.mojom.h"
+#include "chrome/services/sharing/public/mojom/webrtc.mojom.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_utils.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using NearbyConnectionsMojom =
+ location::nearby::connections::mojom::NearbyConnections;
+using NearbyConnectionsHostMojom =
+ location::nearby::connections::mojom::NearbyConnectionsHost;
+
+namespace {
+
+class MockNearbyConnections : public NearbyConnectionsMojom {
+ public:
+};
+
+class FakeSharingMojoService : public sharing::mojom::Sharing {
+ public:
+ FakeSharingMojoService() = default;
+ ~FakeSharingMojoService() override = default;
+
+ // sharing::mojom::Sharing:
+ void CreateSharingWebRtcConnection(
+ mojo::PendingRemote<sharing::mojom::SignallingSender>,
+ mojo::PendingReceiver<sharing::mojom::SignallingReceiver>,
+ mojo::PendingRemote<sharing::mojom::SharingWebRtcConnectionDelegate>,
+ mojo::PendingReceiver<sharing::mojom::SharingWebRtcConnection>,
+ mojo::PendingRemote<network::mojom::P2PSocketManager>,
+ mojo::PendingRemote<network::mojom::MdnsResponder>,
+ std::vector<sharing::mojom::IceServerPtr>) override {
+ NOTIMPLEMENTED();
+ }
+ void CreateNearbyConnections(
+ mojo::PendingRemote<NearbyConnectionsHostMojom> host,
+ CreateNearbyConnectionsCallback callback) override {
+ connections_host.Bind(std::move(host));
+
+ mojo::PendingRemote<NearbyConnectionsMojom> remote;
+ mojo::MakeSelfOwnedReceiver(std::make_unique<MockNearbyConnections>(),
+ remote.InitWithNewPipeAndPassReceiver());
+ std::move(callback).Run(std::move(remote));
+
+ run_loop_connections.Quit();
+ }
+
+ mojo::PendingRemote<sharing::mojom::Sharing> BindSharingService() {
+ return receiver.BindNewPipeAndPassRemote();
+ }
+
+ void WaitForNearbyConnections() { run_loop_connections.Run(); }
+
+ void Reset() { receiver.reset(); }
+
+ private:
+ base::RunLoop run_loop_connections;
+ mojo::Receiver<sharing::mojom::Sharing> receiver{this};
+ mojo::Remote<NearbyConnectionsHostMojom> connections_host;
+};
+
+class MockNearbyProcessManagerObserver : public NearbyProcessManager::Observer {
+ public:
+ MOCK_METHOD1(OnNearbyProfileChanged, void(Profile* profile));
+ MOCK_METHOD0(OnNearbyProcessStarted, void());
+ MOCK_METHOD0(OnNearbyProcessStopped, void());
+};
+
+class NearbyProcessManagerTest : public testing::Test {
+ public:
+ NearbyProcessManagerTest() = default;
+ ~NearbyProcessManagerTest() override = default;
+
+ void SetUp() override { ASSERT_TRUE(testing_profile_manager_.SetUp()); }
+
+ void TearDown() override { DeleteAllProfiles(); }
+
+ Profile* CreateProfile(const std::string& name) {
+ Profile* profile = testing_profile_manager_.CreateTestingProfile(name);
+ profiles_.insert(profile);
+ return profile;
+ }
+
+ void DeleteProfile(Profile* profile) {
+ DoDeleteProfile(profile);
+ profiles_.erase(profile);
+ }
+
+ void DeleteAllProfiles() {
+ for (Profile* profile : profiles_)
+ DoDeleteProfile(profile);
+ profiles_.clear();
+ }
+
+ private:
+ void DoDeleteProfile(Profile* profile) {
+ NearbyProcessManager::GetInstance().OnProfileMarkedForPermanentDeletion(
+ profile);
+ testing_profile_manager_.DeleteTestingProfile(
+ profile->GetProfileUserName());
+ }
+
+ content::BrowserTaskEnvironment task_environment_;
+ content::InProcessUtilityThreadHelper in_process_utility_thread_helper_;
+ TestingProfileManager testing_profile_manager_{
+ TestingBrowserProcess::GetGlobal()};
+ std::set<Profile*> profiles_;
+};
+
+} // namespace
+
+TEST_F(NearbyProcessManagerTest, AddRemoveObserver) {
+ MockNearbyProcessManagerObserver observer;
+ auto& manager = NearbyProcessManager::GetInstance();
+
+ manager.AddObserver(&observer);
+ EXPECT_TRUE(manager.observers_.HasObserver(&observer));
+
+ manager.RemoveObserver(&observer);
+ EXPECT_FALSE(manager.observers_.HasObserver(&observer));
+}
+
+TEST_F(NearbyProcessManagerTest, SetGetActiveProfile) {
+ auto& manager = NearbyProcessManager::GetInstance();
+ Profile* profile = CreateProfile("name");
+ EXPECT_EQ(nullptr, manager.GetActiveProfile());
+
+ manager.SetActiveProfile(profile);
+ ASSERT_NE(nullptr, manager.GetActiveProfile());
+ EXPECT_EQ(profile->GetPath(), manager.GetActiveProfile()->GetPath());
+}
+
+TEST_F(NearbyProcessManagerTest, IsActiveProfile) {
+ auto& manager = NearbyProcessManager::GetInstance();
+ Profile* profile_1 = CreateProfile("name 1");
+ Profile* profile_2 = CreateProfile("name 2");
+ EXPECT_FALSE(manager.IsActiveProfile(profile_1));
+ EXPECT_FALSE(manager.IsActiveProfile(profile_2));
+
+ manager.SetActiveProfile(profile_1);
+ EXPECT_TRUE(manager.IsActiveProfile(profile_1));
+ EXPECT_FALSE(manager.IsActiveProfile(profile_2));
+
+ manager.SetActiveProfile(profile_2);
+ EXPECT_FALSE(manager.IsActiveProfile(profile_1));
+ EXPECT_TRUE(manager.IsActiveProfile(profile_2));
+
+ manager.ClearActiveProfile();
+ EXPECT_FALSE(manager.IsActiveProfile(profile_1));
+ EXPECT_FALSE(manager.IsActiveProfile(profile_2));
+}
+
+TEST_F(NearbyProcessManagerTest, IsAnyProfileActive) {
+ auto& manager = NearbyProcessManager::GetInstance();
+ Profile* profile = CreateProfile("name");
+
+ EXPECT_FALSE(manager.IsAnyProfileActive());
+
+ manager.SetActiveProfile(profile);
+ EXPECT_TRUE(manager.IsAnyProfileActive());
+
+ manager.ClearActiveProfile();
+ EXPECT_FALSE(manager.IsAnyProfileActive());
+}
+
+TEST_F(NearbyProcessManagerTest, OnProfileDeleted_ActiveProfile) {
+ auto& manager = NearbyProcessManager::GetInstance();
+ Profile* profile_1 = CreateProfile("name 1");
+ Profile* profile_2 = CreateProfile("name 2");
+
+ // Set active profile and delete it.
+ manager.SetActiveProfile(profile_1);
+ manager.OnProfileMarkedForPermanentDeletion(profile_1);
+
+ // No profile should be active now.
+ EXPECT_FALSE(manager.IsActiveProfile(profile_1));
+ EXPECT_FALSE(manager.IsActiveProfile(profile_2));
+}
+
+TEST_F(NearbyProcessManagerTest, OnProfileDeleted_InactiveProfile) {
+ auto& manager = NearbyProcessManager::GetInstance();
+ Profile* profile_1 = CreateProfile("name 1");
+ Profile* profile_2 = CreateProfile("name 2");
+
+ // Set active profile and delete inactive one.
+ manager.SetActiveProfile(profile_1);
+ manager.OnProfileMarkedForPermanentDeletion(profile_2);
+
+ // Active profile should still be active.
+ EXPECT_TRUE(manager.IsActiveProfile(profile_1));
+ EXPECT_FALSE(manager.IsActiveProfile(profile_2));
+}
+
+TEST_F(NearbyProcessManagerTest, StartStopProcess) {
+ auto& manager = NearbyProcessManager::GetInstance();
+ Profile* profile = CreateProfile("name");
+ manager.SetActiveProfile(profile);
+
+ MockNearbyProcessManagerObserver observer;
+ base::RunLoop run_loop_started;
+ base::RunLoop run_loop_stopped;
+ EXPECT_CALL(observer, OnNearbyProcessStarted())
+ .WillOnce(testing::Invoke(&run_loop_started, &base::RunLoop::Quit));
+ EXPECT_CALL(observer, OnNearbyProcessStopped())
+ .WillOnce(testing::Invoke(&run_loop_stopped, &base::RunLoop::Quit));
+ manager.AddObserver(&observer);
+
+ // Start up a new process and wait for it to launch.
+ EXPECT_NE(nullptr, manager.GetOrStartNearbyConnections(profile));
+ run_loop_started.Run();
+
+ // Stop the process and wait for it to finish.
+ manager.StopProcess(profile);
+ run_loop_stopped.Run();
+
+ // Active profile should still be active.
+ EXPECT_TRUE(manager.IsActiveProfile(profile));
+
+ manager.RemoveObserver(&observer);
+}
+
+TEST_F(NearbyProcessManagerTest, GetOrStartNearbyConnections) {
+ auto& manager = NearbyProcessManager::GetInstance();
+ Profile* profile = CreateProfile("name");
+ manager.SetActiveProfile(profile);
+
+ // Inject fake Nearby process mojo connection.
+ FakeSharingMojoService fake_sharing_service;
+ manager.BindSharingProcess(fake_sharing_service.BindSharingService());
+
+ // Request a new Nearby Connections interface.
+ EXPECT_NE(nullptr, manager.GetOrStartNearbyConnections(profile));
+ // Expect the manager to bind a new Nearby Connections pipe.
+ fake_sharing_service.WaitForNearbyConnections();
+}
+
+TEST_F(NearbyProcessManagerTest, ResetNearbyProcess) {
+ auto& manager = NearbyProcessManager::GetInstance();
+ Profile* profile = CreateProfile("name");
+ manager.SetActiveProfile(profile);
+
+ // Inject fake Nearby process mojo connection.
+ FakeSharingMojoService fake_sharing_service;
+ manager.BindSharingProcess(fake_sharing_service.BindSharingService());
+
+ MockNearbyProcessManagerObserver observer;
+ base::RunLoop run_loop;
+ EXPECT_CALL(observer, OnNearbyProcessStopped())
+ .WillOnce(testing::Invoke(&run_loop, &base::RunLoop::Quit));
+ manager.AddObserver(&observer);
+
+ // Simulate a dropped mojo connection to the Nearby process.
+ fake_sharing_service.Reset();
+
+ // Expect the OnNearbyProcessStopped() callback to run.
+ run_loop.Run();
+
+ manager.RemoveObserver(&observer);
+}
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_prefs.cc b/chrome/browser/nearby_sharing/nearby_sharing_prefs.cc
index 646028d..5b17e83 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_prefs.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_prefs.cc
@@ -4,10 +4,16 @@
#include "chrome/browser/nearby_sharing/nearby_sharing_prefs.h"
+#include <string>
+
+#include "base/files/file_path.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry.h"
+#include "components/prefs/pref_registry_simple.h"
const char kNearbySharingEnabledPrefName[] = "nearby_sharing.enabled";
+const char kNearbySharingActiveProfilePrefName[] =
+ "nearby_sharing.active_profile";
void RegisterNearbySharingPrefs(user_prefs::PrefRegistrySyncable* registry) {
// This pref is not synced.
@@ -17,3 +23,8 @@
kNearbySharingEnabledPrefName, true /* default_value */,
PrefRegistry::PrefRegistrationFlags::NO_REGISTRATION_FLAGS /* flags */);
}
+
+void RegisterNearbySharingLocalPrefs(PrefRegistrySimple* local_state) {
+ local_state->RegisterFilePathPref(kNearbySharingActiveProfilePrefName,
+ base::FilePath() /* default_value */);
+}
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_prefs.h b/chrome/browser/nearby_sharing/nearby_sharing_prefs.h
index 87965f7..487dbed 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_prefs.h
+++ b/chrome/browser/nearby_sharing/nearby_sharing_prefs.h
@@ -9,8 +9,13 @@
class PrefRegistrySyncable;
} // namespace user_prefs
+class PrefRegistrySimple;
+
extern const char kNearbySharingEnabledPrefName[];
+extern const char kNearbySharingActiveProfilePrefName[];
void RegisterNearbySharingPrefs(user_prefs::PrefRegistrySyncable* registry);
+void RegisterNearbySharingLocalPrefs(PrefRegistrySimple* local_state);
+
#endif // CHROME_BROWSER_NEARBY_SHARING_NEARBY_SHARING_PREFS_H_
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 6c67f2e9..51b7ecbeb 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -241,6 +241,7 @@
#include "chrome/browser/gcm/gcm_product_util.h"
#include "chrome/browser/media/unified_autoplay_config.h"
#include "chrome/browser/metrics/tab_stats_tracker.h"
+#include "chrome/browser/nearby_sharing/nearby_sharing_prefs.h"
#include "chrome/browser/search/instant_service.h"
#include "chrome/browser/search/promos/promo_service.h"
#include "chrome/browser/search/search_suggest/search_suggest_service.h"
@@ -704,6 +705,7 @@
media_router::RegisterLocalStatePrefs(registry);
metrics::TabStatsTracker::RegisterPrefs(registry);
RegisterBrowserPrefs(registry);
+ RegisterNearbySharingLocalPrefs(registry);
StartupBrowserCreator::RegisterLocalStatePrefs(registry);
task_manager::TaskManagerInterface::RegisterPrefs(registry);
UpgradeDetector::RegisterPrefs(registry);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 4f22613..cf80551 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3560,6 +3560,7 @@
"../browser/content_settings/generated_cookie_prefs_unittest.cc",
"../browser/device_identity/device_oauth2_token_service_unittest.cc",
"../browser/nearby_sharing/fast_initiation_manager_unittest.cc",
+ "../browser/nearby_sharing/nearby_process_manager_unittest.cc",
"../browser/profiles/profile_avatar_icon_util_unittest.cc",
"../browser/sharing/webrtc/sharing_service_host_unittest.cc",
"../browser/sharing/webrtc/sharing_webrtc_connection_host_unittest.cc",