 Chromium Code Reviews
 Chromium Code Reviews Issue 1412863004:
  arc-bridge: Add the ARC Bridge Service  (Closed) 
  Base URL: https://chromium.googlesource.com/a/chromium/src.git@master
    
  
    Issue 1412863004:
  arc-bridge: Add the ARC Bridge Service  (Closed) 
  Base URL: https://chromium.googlesource.com/a/chromium/src.git@master| Index: components/arc/arc_bridge_service.cc | 
| diff --git a/components/arc/arc_bridge_service.cc b/components/arc/arc_bridge_service.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..391a73ca255d948cf5948e3f5a952dd64779d7d5 | 
| --- /dev/null | 
| +++ b/components/arc/arc_bridge_service.cc | 
| @@ -0,0 +1,259 @@ | 
| +// Copyright 2015 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 "components/arc/arc_bridge_service.h" | 
| + | 
| +#include "base/files/file_path.h" | 
| +#include "base/files/file_util.h" | 
| +#include "base/prefs/pref_registry_simple.h" | 
| +#include "base/prefs/pref_service.h" | 
| +#include "base/task_runner_util.h" | 
| +#include "base/thread_task_runner_handle.h" | 
| +#include "chromeos/arc/bridge/common/arc_host_messages.h" | 
| +#include "chromeos/arc/bridge/common/arc_instance_messages.h" | 
| +#include "chromeos/dbus/dbus_method_call_status.h" | 
| +#include "chromeos/dbus/dbus_thread_manager.h" | 
| +#include "chromeos/dbus/session_manager_client.h" | 
| +#include "components/arc/arc_pref_names.h" | 
| +#include "ipc/ipc_channel.h" | 
| + | 
| +namespace { | 
| + | 
| +const base::FilePath::CharType kArcBridgeSocketPath[] = | 
| + FILE_PATH_LITERAL("/home/chronos/ArcBridge/bridge.sock"); | 
| + | 
| +} // namespace | 
| + | 
| +namespace arc { | 
| + | 
| +ArcBridgeService::ArcBridgeService( | 
| + const scoped_refptr<base::SequencedTaskRunner>& file_task_runner) | 
| + : ipc_thread_("ARC bridge listener"), | 
| + origin_task_runner_(base::ThreadTaskRunnerHandle::Get()), | 
| + file_task_runner_(file_task_runner), | 
| + observer_list_(new base::ObserverListThreadSafe<Observer>()), | 
| + available_(false), | 
| + enabled_(false), | 
| + state_(ArcBridgeService::STOPPED), | 
| + weak_factory_(this) { | 
| + ipc_thread_.StartWithOptions( | 
| + base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | 
| +} | 
| + | 
| +ArcBridgeService::~ArcBridgeService() {} | 
| + | 
| +void ArcBridgeService::HandleStartup() { | 
| + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | 
| + if (!enabled_) | 
| + return; | 
| + SocketConnect(base::FilePath(kArcBridgeSocketPath)); | 
| +} | 
| + | 
| +void ArcBridgeService::Shutdown() { | 
| + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | 
| + if (state_ == ArcBridgeService::STOPPED || | 
| + state_ == ArcBridgeService::STOPPING) { | 
| + VLOG(1) << "Shutdown() called when ARC is not running"; | 
| + return; | 
| + } | 
| + if (state_ == ArcBridgeService::CONNECTED || | 
| + state_ == ArcBridgeService::CONNECTING) { | 
| + // This was stopped before the D-Bus command to start the instance. Just | 
| + // close the socket (if it was opened). | 
| + if (state_ == ArcBridgeService::CONNECTED) { | 
| + base::AutoLock scoped_lock(ipc_channel_lock_); | 
| + ipc_channel_->Close(); | 
| + ipc_channel_.reset(); | 
| + } | 
| + SetState(ArcBridgeService::STOPPED); | 
| + return; | 
| + } | 
| + | 
| + SetState(ArcBridgeService::STOPPING); | 
| + chromeos::SessionManagerClient* session_manager_client = | 
| + chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); | 
| + session_manager_client->StopArcInstance( | 
| + base::Bind(&ArcBridgeService::OnInstanceStopped, | 
| + weak_factory_.GetWeakPtr())); | 
| +} | 
| + | 
| +void ArcBridgeService::AddObserver(Observer* observer) { | 
| + observer_list_->AddObserver(observer); | 
| +} | 
| + | 
| +void ArcBridgeService::RemoveObserver(Observer* observer) { | 
| + observer_list_->RemoveObserver(observer); | 
| +} | 
| + | 
| +void ArcBridgeService::SetEnabled(bool enabled) { | 
| + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | 
| + | 
| + if (enabled_ == enabled) | 
| + return; | 
| + enabled_ = enabled; | 
| + if (!enabled_ && state_ != ArcBridgeService::STOPPED && | 
| + state_ != ArcBridgeService::STOPPING) | 
| + Shutdown(); | 
| + observer_list_->Notify(FROM_HERE, | 
| + &Observer::OnEnabledChanged, | 
| + enabled_); | 
| +} | 
| + | 
| +bool ArcBridgeService::RegisterInputDevice( | 
| + const std::string& name, const std::string& device_type, | 
| + base::ScopedFD fd) { | 
| + base::AutoLock scoped_lock(ipc_channel_lock_); | 
| 
dcheng
2015/11/06 01:35:28
Consider using clang-format to fix formatting issu
 
lhc(google)
2015/11/06 04:14:16
Done.
 | 
| + if (!ipc_channel_) { | 
| + LOG(ERROR) << "Called RegisterInputDevice when the service is not ready"; | 
| + return false; | 
| + } | 
| + return ipc_channel_->Send(new ArcInstanceMsg_RegisterInputDevice( | 
| + name, device_type, base::FileDescriptor(fd.Pass()))); | 
| +} | 
| + | 
| +void ArcBridgeService::SocketConnect(const base::FilePath& socket_path) { | 
| + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | 
| + if (state_ != ArcBridgeService::STOPPED) { | 
| + VLOG(1) << "SocketConnect() called when instance is not stopped"; | 
| + return; | 
| + } | 
| + SetState(ArcBridgeService::CONNECTING); | 
| + base::PostTaskAndReplyWithResult( | 
| + file_task_runner_.get(), | 
| + FROM_HERE, | 
| + base::Bind(&base::CreateDirectory, socket_path.DirName()), | 
| + base::Bind(&ArcBridgeService::SocketConnectAfterEnsureParentDirectory, | 
| + weak_factory_.GetWeakPtr(), | 
| + socket_path)); | 
| +} | 
| + | 
| +void ArcBridgeService::SocketConnectAfterEnsureParentDirectory( | 
| + const base::FilePath& socket_path, bool directory_present) { | 
| + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | 
| + if (state_ != ArcBridgeService::CONNECTING) { | 
| + VLOG(1) << "Shutdown() called while connecting"; | 
| + return; | 
| + } | 
| + if (!directory_present) { | 
| + LOG(ERROR) << "Error creating directory for " << socket_path.value(); | 
| + Shutdown(); | 
| + return; | 
| + } | 
| + | 
| + if (!Connect(IPC::ChannelHandle(socket_path.value()), | 
| + IPC::Channel::MODE_OPEN_NAMED_SERVER)) { | 
| + LOG(ERROR) << "Error connecting to " << socket_path.value(); | 
| + Shutdown(); | 
| + return; | 
| + } | 
| + | 
| + base::PostTaskAndReplyWithResult( | 
| + file_task_runner_.get(), | 
| + FROM_HERE, | 
| + // TODO(lhchavez): Tighten the security around the socket by tying it to | 
| + // the user the instance will run as. | 
| + base::Bind(&base::SetPosixFilePermissions, socket_path, 0777), | 
| + base::Bind(&ArcBridgeService::SocketConnectAfterSetSocketPermissions, | 
| + weak_factory_.GetWeakPtr(), socket_path)); | 
| +} | 
| + | 
| +void ArcBridgeService::SocketConnectAfterSetSocketPermissions( | 
| + const base::FilePath& socket_path, bool socket_permissions_success) { | 
| + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | 
| + if (state_ != ArcBridgeService::CONNECTED) { | 
| + VLOG(1) << "Shutdown() called while connecting"; | 
| + return; | 
| + } | 
| + | 
| + if (!socket_permissions_success) { | 
| + LOG(ERROR) << "Error setting socket permissions for " | 
| + << socket_path.value(); | 
| + Shutdown(); | 
| + return; | 
| + } | 
| + | 
| + SetState(ArcBridgeService::STARTING); | 
| + chromeos::SessionManagerClient* session_manager_client = | 
| + chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); | 
| + session_manager_client->StartArcInstance( | 
| + socket_path.value(), | 
| + base::Bind(&ArcBridgeService::OnInstanceStarted, | 
| + weak_factory_.GetWeakPtr())); | 
| +} | 
| + | 
| +bool ArcBridgeService::Connect(const IPC::ChannelHandle& handle, | 
| + IPC::Channel::Mode mode) { | 
| + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | 
| + if (state_ != ArcBridgeService::CONNECTING) { | 
| + VLOG(1) << "Shutdown() called while connecting"; | 
| + return false; | 
| + } | 
| + | 
| + base::AutoLock scoped_lock(ipc_channel_lock_); | 
| + ipc_channel_ = IPC::ChannelProxy::Create(handle, mode, this, | 
| 
dcheng
2015/11/06 01:35:28
I don't think this can actually return null.
 
lhc(google)
2015/11/06 04:14:16
Done.
 | 
| + ipc_thread_.task_runner().get()); | 
| + if (!ipc_channel_) | 
| + return false; | 
| + SetState(ArcBridgeService::CONNECTED); | 
| + return true; | 
| +} | 
| + | 
| +void ArcBridgeService::OnInstanceReady() { | 
| + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | 
| + if (state_ != ArcBridgeService::STARTING) { | 
| + VLOG(1) << "Shutdown() called while connecting"; | 
| + return; | 
| + } | 
| + SetState(ArcBridgeService::READY); | 
| +} | 
| + | 
| +void ArcBridgeService::SetState(State state) { | 
| + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | 
| + DCHECK(state_ != state); | 
| + state_ = state; | 
| + observer_list_->Notify(FROM_HERE, | 
| + &Observer::OnStateChanged, | 
| + state_); | 
| +} | 
| + | 
| +bool ArcBridgeService::OnMessageReceived(const IPC::Message& message) { | 
| + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | 
| + bool handled = true; | 
| + | 
| + IPC_BEGIN_MESSAGE_MAP(ArcBridgeService, message) | 
| + IPC_MESSAGE_HANDLER(ArcInstanceHostMsg_InstanceReady, OnInstanceReady) | 
| + IPC_MESSAGE_UNHANDLED(handled = false) | 
| + IPC_END_MESSAGE_MAP() | 
| + | 
| + if (!handled) | 
| + LOG(ERROR) << "Invalid message with type = " << message.type(); | 
| + return handled; | 
| +} | 
| + | 
| +void ArcBridgeService::OnInstanceStarted(bool success) { | 
| + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | 
| + if (state_ != ArcBridgeService::STARTING) { | 
| + VLOG(1) << "Shutdown() called while connecting"; | 
| + return; | 
| + } | 
| + if (!success) { | 
| + LOG(ERROR) << "ARC instance unable to start. Shutting down the bridge"; | 
| + Shutdown(); | 
| + return; | 
| + } | 
| +} | 
| + | 
| +void ArcBridgeService::OnInstanceStopped(bool success) { | 
| + DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | 
| + // STOPPING is the only valid state for this function. | 
| + DCHECK(state_ == ArcBridgeService::STOPPING); | 
| + { | 
| + base::AutoLock scoped_lock(ipc_channel_lock_); | 
| + ipc_channel_->Close(); | 
| + ipc_channel_.reset(); | 
| + } | 
| + SetState(ArcBridgeService::STOPPED); | 
| +} | 
| + | 
| +} // namespace arc |