Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "components/arc/arc_bridge_service.h" | |
| 6 | |
| 7 #include "base/files/file_path.h" | |
| 8 #include "base/files/file_util.h" | |
| 9 #include "base/prefs/pref_registry_simple.h" | |
| 10 #include "base/prefs/pref_service.h" | |
| 11 #include "base/task_runner_util.h" | |
| 12 #include "base/thread_task_runner_handle.h" | |
| 13 #include "chromeos/arc/bridge/common/arc_host_messages.h" | |
| 14 #include "chromeos/arc/bridge/common/arc_instance_messages.h" | |
| 15 #include "chromeos/dbus/arc_instance_client.h" | |
| 16 #include "chromeos/dbus/dbus_method_call_status.h" | |
| 17 #include "chromeos/dbus/dbus_thread_manager.h" | |
| 18 #include "components/arc/arc_pref_names.h" | |
| 19 #include "ipc/ipc_channel.h" | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 const base::FilePath::CharType kArcBridgeSocketPath[] = | |
| 24 FILE_PATH_LITERAL("/home/chronos/ArcBridge/bridge.sock"); | |
| 25 | |
| 26 // Ensures the parent directory of |socket_path| is present. Returns true if it | |
| 27 // was created or if it was already present. | |
| 28 bool EnsureParentDirectoryPresent(const base::FilePath& socket_path) { | |
| 29 base::FilePath path(socket_path); | |
| 30 return base::CreateDirectory(path.DirName()); | |
| 31 } | |
| 32 | |
| 33 // Changes the permissions on the socket so that the ARC instance can use it. | |
| 34 bool SetSocketPermissions(const base::FilePath& socket_path) { | |
| 35 // TODO(lhchavez): Tighten the security around the socket by tying it to the | |
|
Shuhei Takahashi
2015/10/28 21:13:57
Could you file a bug so we don't forget this?
Luis Héctor Chávez
2015/10/28 23:12:20
It's part of the same bug.
| |
| 36 // user the instance will run as. | |
| 37 return HANDLE_EINTR(chmod(socket_path.value().c_str(), 0777)) == 0; | |
| 38 } | |
| 39 | |
| 40 } // namespace | |
| 41 | |
| 42 namespace arc { | |
| 43 | |
| 44 ArcBridgeService::ArcBridgeService( | |
| 45 const scoped_refptr<base::SequencedTaskRunner>& file_task_runner) | |
| 46 : ipc_thread_("ARC bridge listener"), | |
| 47 origin_task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
| 48 file_task_runner_(file_task_runner), | |
| 49 available_(false), | |
|
Shuhei Takahashi
2015/10/28 21:13:57
Hmm,I don't see any code setting available_ to tru
Luis Héctor Chávez
2015/10/28 23:12:20
It's not there yet, but I want to have it availabl
| |
| 50 enabled_(false), | |
| 51 state_(ArcBridgeService::STOPPED), | |
| 52 weak_factory_(this) { | |
| 53 ipc_thread_.StartWithOptions( | |
| 54 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | |
| 55 } | |
| 56 | |
| 57 ArcBridgeService::~ArcBridgeService() {} | |
| 58 | |
| 59 // static | |
| 60 void ArcBridgeService::RegisterPrefs(PrefRegistrySimple* registry) { | |
| 61 registry->RegisterBooleanPref(prefs::kArcEnabled, false); | |
| 62 } | |
| 63 | |
| 64 // static | |
| 65 bool ArcBridgeService::GetEnabledPref(PrefService* pref_service) { | |
| 66 // TODO(lhchavez): Once this is user-configurable, use the real pref. | |
| 67 return true; | |
| 68 } | |
| 69 | |
| 70 void ArcBridgeService::HandleStartup() { | |
| 71 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
| 72 if (!enabled_ || state_ != ArcBridgeService::STOPPED) | |
| 73 return; | |
| 74 base::PostTaskAndReplyWithResult( | |
| 75 file_task_runner_.get(), | |
| 76 FROM_HERE, | |
| 77 base::Bind(&EnsureParentDirectoryPresent, | |
| 78 base::FilePath(kArcBridgeSocketPath)), | |
| 79 base::Bind(&ArcBridgeService::SocketConnect, | |
| 80 weak_factory_.GetWeakPtr(), | |
| 81 base::FilePath(kArcBridgeSocketPath))); | |
| 82 } | |
| 83 | |
| 84 void ArcBridgeService::Shutdown() { | |
| 85 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
| 86 if (state_ != ArcBridgeService::CONNECTED && | |
| 87 state_ != ArcBridgeService::STARTING && | |
| 88 state_ != ArcBridgeService::READY) | |
| 89 return; | |
| 90 ipc_channel_->Close(); | |
|
Shuhei Takahashi
2015/10/28 21:13:57
Could you set ipc_channel_ to null?
Luis Héctor Chávez
2015/10/28 23:12:20
Done.
| |
| 91 base::PostTaskAndReplyWithResult( | |
| 92 file_task_runner_.get(), | |
| 93 FROM_HERE, | |
| 94 base::Bind(&base::DeleteFile, base::FilePath(kArcBridgeSocketPath), | |
| 95 false), | |
| 96 base::Bind(&ArcBridgeService::FinishShutdownAfterSocketDeleted, | |
| 97 weak_factory_.GetWeakPtr())); | |
| 98 } | |
| 99 | |
| 100 void ArcBridgeService::AddObserver(Observer* observer) { | |
| 101 observer_list_.AddObserver(observer); | |
|
Shuhei Takahashi
2015/10/28 21:13:57
ObserverList is not thread-safe. Could you insert
Luis Héctor Chávez
2015/10/28 23:12:20
Done.
| |
| 102 } | |
| 103 | |
| 104 void ArcBridgeService::RemoveObserver(Observer* observer) { | |
| 105 observer_list_.RemoveObserver(observer); | |
| 106 } | |
| 107 | |
| 108 void ArcBridgeService::SetEnabled(bool enabled) { | |
| 109 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
| 110 | |
| 111 if (enabled_ == enabled) | |
| 112 return; | |
| 113 enabled_ = enabled; | |
| 114 if (!enabled_) | |
| 115 Shutdown(); | |
|
Shuhei Takahashi
2015/10/28 21:13:57
Shutdown() will fail when state_ is either STOPPED
Luis Héctor Chávez
2015/10/28 23:12:20
Good catch. Done.
The lifetime of ArcBridgeServic
| |
| 116 FOR_EACH_OBSERVER(Observer, observer_list_, OnEnabledChanged(enabled_)); | |
| 117 } | |
| 118 | |
| 119 bool ArcBridgeService::RegisterInputDevice( | |
| 120 const std::string& name, const std::string& device_type, | |
| 121 base::ScopedFD fd) { | |
| 122 return ipc_channel_->Send(new ArcInstanceMsg_RegisterInputDevice( | |
|
Shuhei Takahashi
2015/10/28 21:13:57
Could you check state_? In some cases ipc_channel_
Luis Héctor Chávez
2015/10/28 23:12:20
Done.
| |
| 123 name, device_type, base::FileDescriptor(fd.Pass()))); | |
| 124 } | |
| 125 | |
| 126 void ArcBridgeService::SocketConnect(const base::FilePath& socket_path, | |
| 127 bool directory_creation_success) { | |
| 128 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
| 129 | |
| 130 if (!directory_creation_success) { | |
| 131 LOG(ERROR) << "Error creating directory for " << socket_path.value(); | |
| 132 NotifyStateChanged(ArcBridgeService::ERROR); | |
| 133 return; | |
| 134 } | |
| 135 | |
| 136 if (!Connect(IPC::ChannelHandle(socket_path.value()), | |
| 137 IPC::Channel::MODE_OPEN_NAMED_SERVER)) { | |
| 138 LOG(ERROR) << "Error connecting to " << socket_path.value(); | |
| 139 NotifyStateChanged(ArcBridgeService::ERROR); | |
| 140 return; | |
| 141 } | |
| 142 | |
| 143 base::PostTaskAndReplyWithResult( | |
| 144 file_task_runner_.get(), | |
| 145 FROM_HERE, | |
| 146 base::Bind(&SetSocketPermissions, socket_path), | |
| 147 base::Bind(&ArcBridgeService::FinishConnectAfterSetSocketPermissions, | |
| 148 weak_factory_.GetWeakPtr(), socket_path)); | |
| 149 } | |
| 150 | |
| 151 void ArcBridgeService::FinishConnectAfterSetSocketPermissions( | |
| 152 const base::FilePath& socket_path, bool socket_permissions_success) { | |
| 153 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
| 154 | |
| 155 if (!socket_permissions_success) { | |
| 156 LOG(ERROR) << "Error setting socket permissions for " | |
| 157 << socket_path.value(); | |
| 158 NotifyStateChanged(ArcBridgeService::ERROR); | |
| 159 return; | |
| 160 } | |
| 161 | |
| 162 // This will fail if the ArcInstanceService is not running on Chrome OS. | |
| 163 // Use base::Unretained since this is a KeyedService. | |
|
Shuhei Takahashi
2015/10/28 21:13:57
Unretained seems not used here?
Luis Héctor Chávez
2015/10/28 23:12:20
Missed that one. Done.
| |
| 164 chromeos::DBusThreadManager::Get()->GetArcInstanceClient()->StartInstance( | |
| 165 socket_path.value(), | |
| 166 base::Bind(&ArcBridgeService::OnInstanceStarted, | |
| 167 weak_factory_.GetWeakPtr())); | |
| 168 } | |
| 169 | |
| 170 | |
| 171 bool ArcBridgeService::Connect(const IPC::ChannelHandle& handle, | |
| 172 IPC::Channel::Mode mode) { | |
| 173 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
| 174 | |
| 175 ipc_channel_ = IPC::ChannelProxy::Create(handle, mode, this, | |
| 176 ipc_thread_.task_runner().get()); | |
| 177 if (!ipc_channel_) | |
| 178 return false; | |
| 179 NotifyStateChanged(ArcBridgeService::CONNECTED); | |
|
Shuhei Takahashi
2015/10/28 21:13:57
It is confusing that state is changed only when IP
Luis Héctor Chávez
2015/10/28 23:12:20
Connect is also used from tests, so it needs to st
| |
| 180 return true; | |
| 181 } | |
| 182 | |
| 183 void ArcBridgeService::OnInstanceReady() { | |
| 184 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
| 185 NotifyStateChanged(ArcBridgeService::READY); | |
| 186 } | |
| 187 | |
| 188 void ArcBridgeService::NotifyStateChanged(State state) { | |
|
Shuhei Takahashi
2015/10/28 21:13:57
This function does set the current state, not only
Luis Héctor Chávez
2015/10/28 23:12:20
Done.
| |
| 189 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
| 190 if (state_ == state) | |
| 191 return; | |
| 192 state_ = state; | |
| 193 FOR_EACH_OBSERVER(Observer, observer_list_, OnStateChanged(state_)); | |
| 194 } | |
| 195 | |
| 196 void ArcBridgeService::FinishShutdownAfterSocketDeleted(bool socket_deleted) { | |
| 197 if (state_ != ArcBridgeService::STARTING && state_ != ArcBridgeService::READY) | |
| 198 return; | |
|
Shuhei Takahashi
2015/10/28 21:13:57
In this code path, state_ is not reset to STOPPED.
Luis Héctor Chávez
2015/10/28 23:12:20
It still needs to wait for the DBus message to ret
| |
| 199 chromeos::DBusThreadManager::Get()->GetArcInstanceClient()->StopInstance( | |
| 200 base::Bind(&ArcBridgeService::OnInstanceStopped, | |
| 201 weak_factory_.GetWeakPtr())); | |
| 202 } | |
| 203 | |
| 204 bool ArcBridgeService::OnMessageReceived(const IPC::Message& message) { | |
| 205 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
| 206 bool handled = true; | |
| 207 | |
| 208 IPC_BEGIN_MESSAGE_MAP(ArcBridgeService, message) | |
| 209 IPC_MESSAGE_HANDLER(ArcInstanceHostMsg_InstanceReady, OnInstanceReady) | |
| 210 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 211 IPC_END_MESSAGE_MAP() | |
| 212 | |
| 213 if (!handled) | |
| 214 LOG(ERROR) << "Invalid message with type = " << message.type(); | |
| 215 return handled; | |
| 216 } | |
| 217 | |
| 218 void ArcBridgeService::OnInstanceStarted( | |
|
Shuhei Takahashi
2015/10/28 21:13:57
Could you swap the position of OnInstanceStarted()
Luis Héctor Chávez
2015/10/28 23:12:20
I don't want to reorder any further until it's rea
| |
| 219 chromeos::DBusMethodCallStatus status) { | |
| 220 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
| 221 if (status != chromeos::DBUS_METHOD_CALL_SUCCESS) { | |
| 222 LOG(ERROR) << "ARC instance unable to start. Shutting down the bridge"; | |
| 223 Shutdown(); | |
| 224 return; | |
| 225 } | |
| 226 NotifyStateChanged(ArcBridgeService::STARTING); | |
| 227 } | |
| 228 | |
| 229 void ArcBridgeService::OnInstanceStopped( | |
| 230 chromeos::DBusMethodCallStatus status) { | |
| 231 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); | |
| 232 NotifyStateChanged(ArcBridgeService::STOPPED); | |
| 233 } | |
| 234 | |
| 235 } // namespace arc | |
| OLD | NEW |