blob: f589ed3d3a13fc11ae87413c3e993e3751fd4fed [file] [log] [blame]
lhchavezf1c055372015-12-07 22:51:181// 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
hidehiko88160c82016-10-18 09:43:475#include "components/arc/arc_session.h"
lhchavezf1c055372015-12-07 22:51:186
7#include <fcntl.h>
lhchavezd896c6d2016-02-23 19:45:428#include <grp.h>
hidehiko321d423c2016-09-30 15:51:309#include <poll.h>
lhchavezd896c6d2016-02-23 19:45:4210#include <unistd.h>
lhchavezf1c055372015-12-07 22:51:1811
lhchavez50abf80a2017-01-25 01:04:5312#include <string>
lhchavezf1c055372015-12-07 22:51:1813#include <utility>
14
15#include "base/files/file_path.h"
16#include "base/files/file_util.h"
17#include "base/location.h"
avibc5337b2015-12-25 23:16:3318#include "base/macros.h"
dchenga0ee5fb82016-04-26 02:46:5519#include "base/memory/ptr_util.h"
lhchavez47e47272016-02-01 21:09:2020#include "base/posix/eintr_wrapper.h"
nya1821ddc2016-07-19 05:19:3921#include "base/sys_info.h"
lhchavezf1c055372015-12-07 22:51:1822#include "base/task_runner_util.h"
lhchavezf1c055372015-12-07 22:51:1823#include "base/threading/thread_checker.h"
gab7966d312016-05-11 20:35:0124#include "base/threading/thread_task_runner_handle.h"
xiaohuic6bc1dc02017-05-08 17:52:0125#include "chromeos/chromeos_switches.h"
nya73ba2132016-06-07 05:21:2826#include "chromeos/cryptohome/cryptohome_parameters.h"
lhchavezf1c055372015-12-07 22:51:1827#include "chromeos/dbus/dbus_method_call_status.h"
28#include "chromeos/dbus/dbus_thread_manager.h"
29#include "chromeos/dbus/session_manager_client.h"
hidehikoa028d092016-10-05 02:20:2330#include "components/arc/arc_bridge_host_impl.h"
xzhou938368c2016-10-04 04:33:1731#include "components/arc/arc_features.h"
nya73ba2132016-06-07 05:21:2832#include "components/user_manager/user_manager.h"
lhchavez47e47272016-02-01 21:09:2033#include "mojo/edk/embedder/embedder.h"
sammccde362062016-11-23 02:33:5734#include "mojo/edk/embedder/named_platform_handle.h"
35#include "mojo/edk/embedder/named_platform_handle_utils.h"
Ken Rockotea1716a02017-05-11 05:49:1036#include "mojo/edk/embedder/outgoing_broker_client_invitation.h"
lhchavez47e47272016-02-01 21:09:2037#include "mojo/edk/embedder/platform_channel_pair.h"
38#include "mojo/edk/embedder/platform_channel_utils_posix.h"
39#include "mojo/edk/embedder/platform_handle_vector.h"
40#include "mojo/edk/embedder/scoped_platform_handle.h"
lhchavezf1c055372015-12-07 22:51:1841#include "mojo/public/cpp/bindings/binding.h"
lhchavezf1c055372015-12-07 22:51:1842
43namespace arc {
44
45namespace {
46
hidehiko14f70fe2016-10-07 16:13:5447using StartArcInstanceResult =
48 chromeos::SessionManagerClient::StartArcInstanceResult;
49
lhchavezf1c055372015-12-07 22:51:1850const base::FilePath::CharType kArcBridgeSocketPath[] =
dgreid500afee2016-01-05 18:16:5451 FILE_PATH_LITERAL("/var/run/chrome/arc_bridge.sock");
lhchavezf1c055372015-12-07 22:51:1852
lhchavezd896c6d2016-02-23 19:45:4253const char kArcBridgeSocketGroup[] = "arc-bridge";
54
lhchavezb078b992016-06-17 17:59:2355// This is called when StopArcInstance D-Bus method completes. Since we have the
56// ArcInstanceStopped() callback and are notified if StartArcInstance fails, we
57// don't need to do anything when StopArcInstance completes.
58void DoNothingInstanceStopped(bool) {}
59
60chromeos::SessionManagerClient* GetSessionManagerClient() {
61 // If the DBusThreadManager or the SessionManagerClient aren't available,
62 // there isn't much we can do. This should only happen when running tests.
63 if (!chromeos::DBusThreadManager::IsInitialized() ||
64 !chromeos::DBusThreadManager::Get() ||
65 !chromeos::DBusThreadManager::Get()->GetSessionManagerClient())
66 return nullptr;
67 return chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
68}
69
hidehiko321d423c2016-09-30 15:51:3070// Creates a pipe. Returns true on success, otherwise false.
71// On success, |read_fd| will be set to the fd of the read side, and
72// |write_fd| will be set to the one of write side.
73bool CreatePipe(base::ScopedFD* read_fd, base::ScopedFD* write_fd) {
74 int fds[2];
75 if (pipe2(fds, O_NONBLOCK | O_CLOEXEC) < 0) {
76 PLOG(ERROR) << "pipe2()";
77 return false;
78 }
79
80 read_fd->reset(fds[0]);
81 write_fd->reset(fds[1]);
82 return true;
83}
84
85// Waits until |raw_socket_fd| is readable.
86// The operation may be cancelled originally triggered by user interaction to
87// disable ARC, or ARC instance is unexpectedly stopped (e.g. crash).
88// To notify such a situation, |raw_cancel_fd| is also passed to here, and the
89// write side will be closed in such a case.
90bool WaitForSocketReadable(int raw_socket_fd, int raw_cancel_fd) {
91 struct pollfd fds[2] = {
92 {raw_socket_fd, POLLIN, 0}, {raw_cancel_fd, POLLIN, 0},
93 };
94
95 if (HANDLE_EINTR(poll(fds, arraysize(fds), -1)) <= 0) {
96 PLOG(ERROR) << "poll()";
97 return false;
98 }
99
100 if (fds[1].revents) {
101 // Notified that Stop() is invoked. Cancel the Mojo connecting.
102 VLOG(1) << "Stop() was called during ConnectMojo()";
103 return false;
104 }
105
106 DCHECK(fds[0].revents);
107 return true;
108}
109
110// TODO(hidehiko): Refactor more to make this class unittest-able, for at least
111// state-machine part.
hidehiko88160c82016-10-18 09:43:47112class ArcSessionImpl : public ArcSession,
113 public chromeos::SessionManagerClient::Observer {
lhchavezf1c055372015-12-07 22:51:18114 public:
hidehiko88160c82016-10-18 09:43:47115 // The possible states of the session. In the normal flow, the state changes
116 // in the following sequence:
lhchavezf1c055372015-12-07 22:51:18117 //
hidehiko321d423c2016-09-30 15:51:30118 // NOT_STARTED
lhchavezf1c055372015-12-07 22:51:18119 // Start() ->
hidehiko321d423c2016-09-30 15:51:30120 // CREATING_SOCKET
lhchavezf1c055372015-12-07 22:51:18121 // CreateSocket() -> OnSocketCreated() ->
hidehiko321d423c2016-09-30 15:51:30122 // STARTING_INSTANCE
123 // -> OnInstanceStarted() ->
124 // CONNECTING_MOJO
125 // ConnectMojo() -> OnMojoConnected() ->
126 // RUNNING
lhchavezf1c055372015-12-07 22:51:18127 //
hidehiko321d423c2016-09-30 15:51:30128 // At any state, Stop() can be called. It does not immediately stop the
129 // instance, but will eventually stop it.
hidehikoae82e3a2017-03-01 20:05:39130 // The actual stop will be notified via ArcSession::Observer::OnStopped().
lhchavezf1c055372015-12-07 22:51:18131 //
hidehiko321d423c2016-09-30 15:51:30132 // When Stop() is called, it makes various behavior based on the current
133 // phase.
lhchavezaff66ee2016-01-05 19:43:13134 //
hidehiko321d423c2016-09-30 15:51:30135 // NOT_STARTED:
136 // Do nothing. Immediately transition to the STOPPED state.
hidehiko14f70fe2016-10-07 16:13:54137 // CREATING_SOCKET:
hidehiko13622872016-10-21 10:05:09138 // The main task of the phase runs on BlockingPool thread. So, Stop() just
hidehiko14f70fe2016-10-07 16:13:54139 // sets the flag and return. On the main task completion, a callback
hidehiko321d423c2016-09-30 15:51:30140 // will run on the main (practically UI) thread, and the flag is checked
141 // at the beginning of them. This should work under the assumption that
142 // the main tasks do not block indefinitely.
143 // STARTING_INSTANCE:
144 // The ARC instance is starting via SessionManager. So, similar to
hidehiko14f70fe2016-10-07 16:13:54145 // CREATING_SOCKET case, Stop() just sets the flag and return. In its
146 // callback, it checks if ARC instance is successfully started or not.
147 // In case of success, a request to stop the ARC instance is sent to
148 // SessionManager. Its completion will be notified via ArcInstanceStopped.
149 // Otherwise, it just turns into STOPPED state.
hidehiko321d423c2016-09-30 15:51:30150 // CONNECTING_MOJO:
hidehiko13622872016-10-21 10:05:09151 // The main task runs on BlockingPool thread, but it is blocking call.
hidehiko321d423c2016-09-30 15:51:30152 // So, Stop() sends a request to cancel the blocking by closing the pipe
153 // whose read side is also polled. Then, in its callback, similar to
154 // STARTING_INSTANCE, a request to stop the ARC instance is sent to
155 // SessionManager, and ArcInstanceStopped handles remaining procedure.
156 // RUNNING:
157 // There is no more callback which runs on normal flow, so Stop() requests
158 // to stop the ARC instance via SessionManager.
159 //
160 // Another trigger to change the state coming from outside of this class
161 // is an event ArcInstanceStopped() sent from SessionManager, when ARC
162 // instace unexpectedly terminates. ArcInstanceStopped() turns the state into
163 // STOPPED immediately.
164 // This happens only when STARTING_INSTANCE, CONNECTING_MOJO or RUNNING
165 // state.
166 //
167 // STARTING_INSTANCE:
168 // In OnInstanceStarted(), |state_| is checked at the beginning. If it is
169 // STOPPED, then ArcInstanceStopped() is called. Do nothing in that case.
170 // CONNECTING_MOJO:
171 // Similar to Stop() case above, ArcInstanceStopped() also notifies to
hidehiko13622872016-10-21 10:05:09172 // BlockingPool thread to cancel it to unblock the thread. In
hidehiko321d423c2016-09-30 15:51:30173 // OnMojoConnected(), similar to OnInstanceStarted(), check if |state_| is
174 // STOPPED, then do nothing.
175 // RUNNING:
176 // It is not necessary to do anything special here.
177 //
178 // In NOT_STARTED or STOPPED state, the instance can be safely destructed.
179 // Specifically, in STOPPED state, there may be inflight operations or
180 // pending callbacks. Though, what they do is just do-nothing conceptually
181 // and they can be safely ignored.
nya0c9dabf2016-07-13 19:54:27182 //
183 // Note: Order of constants below matters. Please make sure to sort them
184 // in chronological order.
lhchavezf1c055372015-12-07 22:51:18185 enum class State {
hidehiko321d423c2016-09-30 15:51:30186 // ARC is not yet started.
187 NOT_STARTED,
lhchavezf1c055372015-12-07 22:51:18188
189 // An UNIX socket is being created.
hidehiko321d423c2016-09-30 15:51:30190 CREATING_SOCKET,
lhchavezf1c055372015-12-07 22:51:18191
192 // The request to start the instance has been sent.
hidehiko321d423c2016-09-30 15:51:30193 STARTING_INSTANCE,
lhchavezf1c055372015-12-07 22:51:18194
195 // The instance has started. Waiting for it to connect to the IPC bridge.
hidehiko321d423c2016-09-30 15:51:30196 CONNECTING_MOJO,
lhchavezf1c055372015-12-07 22:51:18197
hidehiko321d423c2016-09-30 15:51:30198 // The instance is fully set up.
199 RUNNING,
lhchavezf1c055372015-12-07 22:51:18200
hidehiko321d423c2016-09-30 15:51:30201 // ARC is terminated.
202 STOPPED,
lhchavezf1c055372015-12-07 22:51:18203 };
204
hidehiko01319c4e2016-12-08 05:59:46205 ArcSessionImpl(ArcBridgeService* arc_bridge_service,
206 const scoped_refptr<base::TaskRunner>& blocking_task_runner);
hidehiko88160c82016-10-18 09:43:47207 ~ArcSessionImpl() override;
lhchavezf1c055372015-12-07 22:51:18208
hidehiko88160c82016-10-18 09:43:47209 // ArcSession overrides:
lhchavezf1c055372015-12-07 22:51:18210 void Start() override;
211 void Stop() override;
hidehiko13622872016-10-21 10:05:09212 void OnShutdown() override;
lhchavezf1c055372015-12-07 22:51:18213
214 private:
hidehiko88160c82016-10-18 09:43:47215 // Creates the UNIX socket on a worker pool and then processes its file
216 // descriptor.
sammccde362062016-11-23 02:33:57217 static mojo::edk::ScopedPlatformHandle CreateSocket();
218 void OnSocketCreated(mojo::edk::ScopedPlatformHandle fd);
lhchavezf1c055372015-12-07 22:51:18219
hidehiko321d423c2016-09-30 15:51:30220 // DBus callback for StartArcInstance().
sammccde362062016-11-23 02:33:57221 void OnInstanceStarted(mojo::edk::ScopedPlatformHandle socket_fd,
hidehiko02a11f52017-05-22 08:43:06222 StartArcInstanceResult result,
223 const std::string& container_instance_id);
hidehiko321d423c2016-09-30 15:51:30224
lhchavezf1c055372015-12-07 22:51:18225 // Synchronously accepts a connection on |socket_fd| and then processes the
226 // connected socket's file descriptor.
lhchavez50abf80a2017-01-25 01:04:53227 static mojo::ScopedMessagePipeHandle ConnectMojo(
sammccde362062016-11-23 02:33:57228 mojo::edk::ScopedPlatformHandle socket_fd,
229 base::ScopedFD cancel_fd);
lhchavez50abf80a2017-01-25 01:04:53230 void OnMojoConnected(mojo::ScopedMessagePipeHandle server_pipe);
lhchavezf1c055372015-12-07 22:51:18231
hidehiko321d423c2016-09-30 15:51:30232 // Request to stop ARC instance via DBus.
233 void StopArcInstance();
lhchavezb078b992016-06-17 17:59:23234
235 // chromeos::SessionManagerClient::Observer:
hidehiko02a11f52017-05-22 08:43:06236 void ArcInstanceStopped(bool clean,
237 const std::string& container_instance_id) override;
lhchavezf1c055372015-12-07 22:51:18238
hidehiko321d423c2016-09-30 15:51:30239 // Completes the termination procedure.
hidehikoae82e3a2017-03-01 20:05:39240 void OnStopped(ArcStopReason reason);
lhchavezf1c055372015-12-07 22:51:18241
hidehiko13622872016-10-21 10:05:09242 // Checks whether a function runs on the thread where the instance is
243 // created.
Hidehiko Abea0cfefae2017-06-22 19:30:11244 THREAD_CHECKER(thread_checker_);
hidehiko13622872016-10-21 10:05:09245
hidehiko01319c4e2016-12-08 05:59:46246 // Owned by ArcServiceManager.
247 ArcBridgeService* const arc_bridge_service_;
248
hidehiko13622872016-10-21 10:05:09249 // Task runner to run a blocking tasks.
250 scoped_refptr<base::TaskRunner> blocking_task_runner_;
251
hidehiko88160c82016-10-18 09:43:47252 // The state of the session.
hidehiko321d423c2016-09-30 15:51:30253 State state_ = State::NOT_STARTED;
254
255 // When Stop() is called, this flag is set.
256 bool stop_requested_ = false;
257
hidehiko02a11f52017-05-22 08:43:06258 // Container instance id passed from session_manager.
259 // Should be available only after OnInstanceStarted().
260 std::string container_instance_id_;
261
hidehiko321d423c2016-09-30 15:51:30262 // In CONNECTING_MOJO state, this is set to the write side of the pipe
263 // to notify cancelling of the procedure.
264 base::ScopedFD accept_cancel_pipe_;
nya0c9dabf2016-07-13 19:54:27265
hidehikoa028d092016-10-05 02:20:23266 // Mojo endpoint.
267 std::unique_ptr<mojom::ArcBridgeHost> arc_bridge_host_;
268
lhchavezf1c055372015-12-07 22:51:18269 // WeakPtrFactory to use callbacks.
hidehiko88160c82016-10-18 09:43:47270 base::WeakPtrFactory<ArcSessionImpl> weak_factory_;
lhchavezf1c055372015-12-07 22:51:18271
272 private:
hidehiko88160c82016-10-18 09:43:47273 DISALLOW_COPY_AND_ASSIGN(ArcSessionImpl);
lhchavezf1c055372015-12-07 22:51:18274};
275
hidehiko13622872016-10-21 10:05:09276ArcSessionImpl::ArcSessionImpl(
hidehiko01319c4e2016-12-08 05:59:46277 ArcBridgeService* arc_bridge_service,
hidehiko13622872016-10-21 10:05:09278 const scoped_refptr<base::TaskRunner>& blocking_task_runner)
hidehiko01319c4e2016-12-08 05:59:46279 : arc_bridge_service_(arc_bridge_service),
280 blocking_task_runner_(blocking_task_runner),
281 weak_factory_(this) {
lhchavezb078b992016-06-17 17:59:23282 chromeos::SessionManagerClient* client = GetSessionManagerClient();
283 if (client == nullptr)
284 return;
285 client->AddObserver(this);
286}
lhchavezf1c055372015-12-07 22:51:18287
hidehiko88160c82016-10-18 09:43:47288ArcSessionImpl::~ArcSessionImpl() {
Hidehiko Abea0cfefae2017-06-22 19:30:11289 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
hidehiko13622872016-10-21 10:05:09290 DCHECK(state_ == State::NOT_STARTED || state_ == State::STOPPED);
lhchavezb078b992016-06-17 17:59:23291 chromeos::SessionManagerClient* client = GetSessionManagerClient();
292 if (client == nullptr)
293 return;
294 client->RemoveObserver(this);
lhchavezf1c055372015-12-07 22:51:18295}
296
hidehiko88160c82016-10-18 09:43:47297void ArcSessionImpl::Start() {
Hidehiko Abea0cfefae2017-06-22 19:30:11298 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
hidehiko321d423c2016-09-30 15:51:30299 DCHECK_EQ(state_, State::NOT_STARTED);
300 VLOG(2) << "Starting ARC session.";
hidehiko14f70fe2016-10-07 16:13:54301 VLOG(2) << "Creating socket...";
hidehiko321d423c2016-09-30 15:51:30302
hidehiko321d423c2016-09-30 15:51:30303 state_ = State::CREATING_SOCKET;
lhchavezf1c055372015-12-07 22:51:18304 base::PostTaskAndReplyWithResult(
hidehiko13622872016-10-21 10:05:09305 blocking_task_runner_.get(), FROM_HERE,
hidehiko88160c82016-10-18 09:43:47306 base::Bind(&ArcSessionImpl::CreateSocket),
307 base::Bind(&ArcSessionImpl::OnSocketCreated, weak_factory_.GetWeakPtr()));
lhchavezf1c055372015-12-07 22:51:18308}
309
310// static
sammccde362062016-11-23 02:33:57311mojo::edk::ScopedPlatformHandle ArcSessionImpl::CreateSocket() {
lhchavezf1c055372015-12-07 22:51:18312 base::FilePath socket_path(kArcBridgeSocketPath);
313
sammccde362062016-11-23 02:33:57314 mojo::edk::ScopedPlatformHandle socket_fd = mojo::edk::CreateServerHandle(
315 mojo::edk::NamedPlatformHandle(socket_path.value()));
316 if (!socket_fd.is_valid())
317 return socket_fd;
lhchavezf1c055372015-12-07 22:51:18318
lhchavezd896c6d2016-02-23 19:45:42319 // Change permissions on the socket.
320 struct group arc_bridge_group;
321 struct group* arc_bridge_group_res = nullptr;
322 char buf[10000];
323 if (HANDLE_EINTR(getgrnam_r(kArcBridgeSocketGroup, &arc_bridge_group, buf,
324 sizeof(buf), &arc_bridge_group_res)) < 0) {
325 PLOG(ERROR) << "getgrnam_r";
sammccde362062016-11-23 02:33:57326 return mojo::edk::ScopedPlatformHandle();
lhchavezd896c6d2016-02-23 19:45:42327 }
328
329 if (!arc_bridge_group_res) {
330 LOG(ERROR) << "Group '" << kArcBridgeSocketGroup << "' not found";
sammccde362062016-11-23 02:33:57331 return mojo::edk::ScopedPlatformHandle();
lhchavezd896c6d2016-02-23 19:45:42332 }
333
334 if (HANDLE_EINTR(chown(kArcBridgeSocketPath, -1, arc_bridge_group.gr_gid)) <
335 0) {
336 PLOG(ERROR) << "chown";
sammccde362062016-11-23 02:33:57337 return mojo::edk::ScopedPlatformHandle();
lhchavezd896c6d2016-02-23 19:45:42338 }
339
340 if (!base::SetPosixFilePermissions(socket_path, 0660)) {
lhchavezf1c055372015-12-07 22:51:18341 PLOG(ERROR) << "Could not set permissions: " << socket_path.value();
sammccde362062016-11-23 02:33:57342 return mojo::edk::ScopedPlatformHandle();
lhchavezf1c055372015-12-07 22:51:18343 }
344
345 return socket_fd;
346}
347
sammccde362062016-11-23 02:33:57348void ArcSessionImpl::OnSocketCreated(
349 mojo::edk::ScopedPlatformHandle socket_fd) {
Hidehiko Abea0cfefae2017-06-22 19:30:11350 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
hidehiko321d423c2016-09-30 15:51:30351 DCHECK_EQ(state_, State::CREATING_SOCKET);
352
353 if (stop_requested_) {
lhchavezf1c055372015-12-07 22:51:18354 VLOG(1) << "Stop() called while connecting";
hidehikoae82e3a2017-03-01 20:05:39355 OnStopped(ArcStopReason::SHUTDOWN);
lhchavezf1c055372015-12-07 22:51:18356 return;
357 }
lhchavezf1c055372015-12-07 22:51:18358
359 if (!socket_fd.is_valid()) {
360 LOG(ERROR) << "ARC: Error creating socket";
hidehikoae82e3a2017-03-01 20:05:39361 OnStopped(ArcStopReason::GENERIC_BOOT_FAILURE);
lhchavezf1c055372015-12-07 22:51:18362 return;
363 }
nya73ba2132016-06-07 05:21:28364
hidehiko321d423c2016-09-30 15:51:30365 VLOG(2) << "Socket is created. Starting ARC instance...";
366 state_ = State::STARTING_INSTANCE;
nya73ba2132016-06-07 05:21:28367 user_manager::UserManager* user_manager = user_manager::UserManager::Get();
368 DCHECK(user_manager->GetPrimaryUser());
369 const cryptohome::Identification cryptohome_id(
370 user_manager->GetPrimaryUser()->GetAccountId());
371
xiaohuic2f57e3f72017-05-31 01:29:53372 const bool skip_boot_completed_broadcast =
xzhou938368c2016-10-04 04:33:17373 !base::FeatureList::IsEnabled(arc::kBootCompletedBroadcastFeature);
374
xiaohuic6bc1dc02017-05-08 17:52:01375 // We only enable /vendor/priv-app when voice interaction is enabled because
376 // voice interaction service apk would be bundled in this location.
xiaohuic2f57e3f72017-05-31 01:29:53377 const bool scan_vendor_priv_app =
xiaohuic6bc1dc02017-05-08 17:52:01378 chromeos::switches::IsVoiceInteractionEnabled();
379
lhchavezf1c055372015-12-07 22:51:18380 chromeos::SessionManagerClient* session_manager_client =
381 chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
382 session_manager_client->StartArcInstance(
yusukes3ad400072017-06-09 16:54:34383 chromeos::SessionManagerClient::ArcStartupMode::FULL, cryptohome_id,
384 skip_boot_completed_broadcast, scan_vendor_priv_app,
hidehiko88160c82016-10-18 09:43:47385 base::Bind(&ArcSessionImpl::OnInstanceStarted, weak_factory_.GetWeakPtr(),
386 base::Passed(&socket_fd)));
lhchavezf1c055372015-12-07 22:51:18387}
388
sammccde362062016-11-23 02:33:57389void ArcSessionImpl::OnInstanceStarted(
390 mojo::edk::ScopedPlatformHandle socket_fd,
hidehiko02a11f52017-05-22 08:43:06391 StartArcInstanceResult result,
392 const std::string& container_instance_id) {
Hidehiko Abea0cfefae2017-06-22 19:30:11393 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
hidehiko321d423c2016-09-30 15:51:30394 DCHECK_EQ(state_, State::STARTING_INSTANCE);
hidehiko02a11f52017-05-22 08:43:06395 container_instance_id_ = container_instance_id;
hidehiko321d423c2016-09-30 15:51:30396
397 if (stop_requested_) {
hidehiko14f70fe2016-10-07 16:13:54398 if (result == StartArcInstanceResult::SUCCESS) {
hidehiko321d423c2016-09-30 15:51:30399 // The ARC instance has started to run. Request to stop.
400 StopArcInstance();
401 return;
402 }
hidehikoae82e3a2017-03-01 20:05:39403 OnStopped(ArcStopReason::SHUTDOWN);
hidehiko321d423c2016-09-30 15:51:30404 return;
405 }
406
hidehiko14f70fe2016-10-07 16:13:54407 if (result != StartArcInstanceResult::SUCCESS) {
lhchavezf1c055372015-12-07 22:51:18408 LOG(ERROR) << "Failed to start ARC instance";
hidehiko14f70fe2016-10-07 16:13:54409 OnStopped(result == StartArcInstanceResult::LOW_FREE_DISK_SPACE
hidehikoae82e3a2017-03-01 20:05:39410 ? ArcStopReason::LOW_DISK_SPACE
411 : ArcStopReason::GENERIC_BOOT_FAILURE);
lhchavezf1c055372015-12-07 22:51:18412 return;
413 }
hidehiko321d423c2016-09-30 15:51:30414
415 VLOG(2) << "ARC instance is successfully started. Connecting Mojo...";
416 state_ = State::CONNECTING_MOJO;
417
418 // Prepare a pipe so that AcceptInstanceConnection can be interrupted on
419 // Stop().
420 base::ScopedFD cancel_fd;
421 if (!CreatePipe(&cancel_fd, &accept_cancel_pipe_)) {
hidehikoae82e3a2017-03-01 20:05:39422 OnStopped(ArcStopReason::GENERIC_BOOT_FAILURE);
lhchavezb078b992016-06-17 17:59:23423 return;
424 }
lhchavezf1c055372015-12-07 22:51:18425
426 base::PostTaskAndReplyWithResult(
hidehiko13622872016-10-21 10:05:09427 blocking_task_runner_.get(), FROM_HERE,
hidehiko88160c82016-10-18 09:43:47428 base::Bind(&ArcSessionImpl::ConnectMojo, base::Passed(&socket_fd),
hidehiko321d423c2016-09-30 15:51:30429 base::Passed(&cancel_fd)),
hidehiko88160c82016-10-18 09:43:47430 base::Bind(&ArcSessionImpl::OnMojoConnected, weak_factory_.GetWeakPtr()));
lhchavezf1c055372015-12-07 22:51:18431}
432
433// static
lhchavez50abf80a2017-01-25 01:04:53434mojo::ScopedMessagePipeHandle ArcSessionImpl::ConnectMojo(
sammccde362062016-11-23 02:33:57435 mojo::edk::ScopedPlatformHandle socket_fd,
436 base::ScopedFD cancel_fd) {
437 if (!WaitForSocketReadable(socket_fd.get().handle, cancel_fd.get())) {
hidehiko321d423c2016-09-30 15:51:30438 VLOG(1) << "Mojo connection was cancelled.";
lhchavez50abf80a2017-01-25 01:04:53439 return mojo::ScopedMessagePipeHandle();
hidehiko321d423c2016-09-30 15:51:30440 }
441
sammccde362062016-11-23 02:33:57442 mojo::edk::ScopedPlatformHandle scoped_fd;
443 if (!mojo::edk::ServerAcceptConnection(socket_fd.get(), &scoped_fd,
444 /* check_peer_user = */ false) ||
445 !scoped_fd.is_valid()) {
lhchavez50abf80a2017-01-25 01:04:53446 return mojo::ScopedMessagePipeHandle();
lhchavezf1c055372015-12-07 22:51:18447 }
lhchavez47e47272016-02-01 21:09:20448
kcwue5654af2016-04-16 02:40:52449 // Hardcode pid 0 since it is unused in mojo.
450 const base::ProcessHandle kUnusedChildProcessHandle = 0;
amistry15cf85cc2016-05-26 23:45:35451 mojo::edk::PlatformChannelPair channel_pair;
Ken Rockotea1716a02017-05-11 05:49:10452 mojo::edk::OutgoingBrokerClientInvitation invitation;
453
454 std::string token = mojo::edk::GenerateRandomToken();
455 mojo::ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(token);
456
Ken Rockot29bd77b2017-05-11 22:24:49457 invitation.Send(
458 kUnusedChildProcessHandle,
459 mojo::edk::ConnectionParams(mojo::edk::TransportProtocol::kLegacy,
460 channel_pair.PassServerHandle()));
lhchavez47e47272016-02-01 21:09:20461
462 mojo::edk::ScopedPlatformHandleVectorPtr handles(
amistry15cf85cc2016-05-26 23:45:35463 new mojo::edk::PlatformHandleVector{
464 channel_pair.PassClientHandle().release()});
lhchavez47e47272016-02-01 21:09:20465
lhchavez50abf80a2017-01-25 01:04:53466 // We need to send the length of the message as a single byte, so make sure it
467 // fits.
468 DCHECK_LT(token.size(), 256u);
469 uint8_t message_length = static_cast<uint8_t>(token.size());
470 struct iovec iov[] = {{&message_length, sizeof(message_length)},
471 {const_cast<char*>(token.c_str()), token.size()}};
lhchavez47e47272016-02-01 21:09:20472 ssize_t result = mojo::edk::PlatformChannelSendmsgWithHandles(
lhchavez50abf80a2017-01-25 01:04:53473 scoped_fd.get(), iov, sizeof(iov) / sizeof(iov[0]), handles->data(),
474 handles->size());
lhchavez47e47272016-02-01 21:09:20475 if (result == -1) {
476 PLOG(ERROR) << "sendmsg";
lhchavez50abf80a2017-01-25 01:04:53477 return mojo::ScopedMessagePipeHandle();
lhchavez47e47272016-02-01 21:09:20478 }
479
rockot1039b822017-02-09 23:19:41480 return pipe;
lhchavezf1c055372015-12-07 22:51:18481}
482
lhchavez50abf80a2017-01-25 01:04:53483void ArcSessionImpl::OnMojoConnected(
484 mojo::ScopedMessagePipeHandle server_pipe) {
Hidehiko Abea0cfefae2017-06-22 19:30:11485 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
hidehiko321d423c2016-09-30 15:51:30486 DCHECK_EQ(state_, State::CONNECTING_MOJO);
487 accept_cancel_pipe_.reset();
488
489 if (stop_requested_) {
490 StopArcInstance();
491 return;
492 }
493
lhchavez47e47272016-02-01 21:09:20494 if (!server_pipe.is_valid()) {
495 LOG(ERROR) << "Invalid pipe";
hidehiko321d423c2016-09-30 15:51:30496 StopArcInstance();
lhchavez47e47272016-02-01 21:09:20497 return;
498 }
hidehiko321d423c2016-09-30 15:51:30499
leon.han890322912016-04-16 23:22:53500 mojom::ArcBridgeInstancePtr instance;
501 instance.Bind(mojo::InterfacePtrInfo<mojom::ArcBridgeInstance>(
502 std::move(server_pipe), 0u));
hidehiko01319c4e2016-12-08 05:59:46503 arc_bridge_host_ = base::MakeUnique<ArcBridgeHostImpl>(arc_bridge_service_,
504 std::move(instance));
hidehiko321d423c2016-09-30 15:51:30505
506 VLOG(2) << "Mojo is connected. ARC is running.";
507 state_ = State::RUNNING;
yusukes5e2934b2016-10-12 19:24:14508 for (auto& observer : observer_list_)
hidehiko9bc9ba52016-12-14 09:01:18509 observer.OnSessionReady();
lhchavezf1c055372015-12-07 22:51:18510}
511
hidehiko88160c82016-10-18 09:43:47512void ArcSessionImpl::Stop() {
Hidehiko Abea0cfefae2017-06-22 19:30:11513 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
hidehiko321d423c2016-09-30 15:51:30514 VLOG(2) << "Stopping ARC session is requested.";
515
516 // For second time or later, just do nothing.
517 // It is already in the stopping phase.
518 if (stop_requested_)
lhchavezf1c055372015-12-07 22:51:18519 return;
hidehiko321d423c2016-09-30 15:51:30520
521 stop_requested_ = true;
hidehikoa028d092016-10-05 02:20:23522 arc_bridge_host_.reset();
hidehiko321d423c2016-09-30 15:51:30523 switch (state_) {
524 case State::NOT_STARTED:
hidehikoae82e3a2017-03-01 20:05:39525 OnStopped(ArcStopReason::SHUTDOWN);
hidehiko321d423c2016-09-30 15:51:30526 return;
527
hidehiko321d423c2016-09-30 15:51:30528 case State::CREATING_SOCKET:
529 case State::STARTING_INSTANCE:
530 // Before starting the ARC instance, we do nothing here.
531 // At some point, a callback will be invoked on UI thread,
532 // and stopping procedure will be run there.
533 // On Chrome shutdown, it is not the case because the message loop is
534 // already stopped here. Practically, it is not a problem because;
hidehiko14f70fe2016-10-07 16:13:54535 // - On socket creating, it is ok to simply ignore such cases,
536 // because we no-longer continue the bootstrap procedure.
hidehiko321d423c2016-09-30 15:51:30537 // - On starting instance, the container instance can be leaked.
538 // Practically it is not problematic because the session manager will
539 // clean it up.
540 return;
541
542 case State::CONNECTING_MOJO:
hidehiko13622872016-10-21 10:05:09543 // Mojo connection is being waited on a BlockingPool thread.
hidehiko321d423c2016-09-30 15:51:30544 // Request to cancel it. Following stopping procedure will run
545 // in its callback.
hidehiko321d423c2016-09-30 15:51:30546 accept_cancel_pipe_.reset();
547 return;
548
549 case State::RUNNING:
550 // Now ARC instance is running. Request to stop it.
551 StopArcInstance();
552 return;
553
554 case State::STOPPED:
555 // The instance is already stopped. Do nothing.
556 return;
lhchavezf1c055372015-12-07 22:51:18557 }
hidehiko321d423c2016-09-30 15:51:30558}
559
hidehiko88160c82016-10-18 09:43:47560void ArcSessionImpl::StopArcInstance() {
Hidehiko Abea0cfefae2017-06-22 19:30:11561 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
hidehiko321d423c2016-09-30 15:51:30562 DCHECK(state_ == State::STARTING_INSTANCE ||
563 state_ == State::CONNECTING_MOJO || state_ == State::RUNNING);
564
lhchavezb078b992016-06-17 17:59:23565 // Notification will arrive through ArcInstanceStopped().
hidehiko321d423c2016-09-30 15:51:30566 VLOG(2) << "Requesting to stop ARC instance";
lhchavezf1c055372015-12-07 22:51:18567 chromeos::SessionManagerClient* session_manager_client =
568 chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
lhchavezb078b992016-06-17 17:59:23569 session_manager_client->StopArcInstance(
570 base::Bind(&DoNothingInstanceStopped));
lhchavezf1c055372015-12-07 22:51:18571}
572
hidehiko02a11f52017-05-22 08:43:06573void ArcSessionImpl::ArcInstanceStopped(
574 bool clean,
575 const std::string& container_instance_id) {
Hidehiko Abea0cfefae2017-06-22 19:30:11576 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
hidehiko321d423c2016-09-30 15:51:30577 VLOG(1) << "Notified that ARC instance is stopped "
578 << (clean ? "cleanly" : "uncleanly");
579
hidehiko02a11f52017-05-22 08:43:06580 if (container_instance_id != container_instance_id_) {
581 VLOG(1) << "Container instance id mismatch. Do nothing."
582 << container_instance_id << " vs " << container_instance_id_;
583 return;
584 }
585
586 // Release |container_instance_id_| to avoid duplicate invocation situation.
587 container_instance_id_.clear();
588
hidehiko321d423c2016-09-30 15:51:30589 // In case that crash happens during before the Mojo channel is connected,
hidehiko13622872016-10-21 10:05:09590 // unlock the BlockingPool thread.
hidehiko321d423c2016-09-30 15:51:30591 accept_cancel_pipe_.reset();
592
hidehikoae82e3a2017-03-01 20:05:39593 ArcStopReason reason;
hidehiko321d423c2016-09-30 15:51:30594 if (stop_requested_) {
595 // If the ARC instance is stopped after its explicit request,
596 // return SHUTDOWN.
hidehikoae82e3a2017-03-01 20:05:39597 reason = ArcStopReason::SHUTDOWN;
hidehiko321d423c2016-09-30 15:51:30598 } else if (clean) {
599 // If the ARC instance is stopped, but it is not explicitly requested,
600 // then this is triggered by some failure during the starting procedure.
601 // Return GENERIC_BOOT_FAILURE for the case.
hidehikoae82e3a2017-03-01 20:05:39602 reason = ArcStopReason::GENERIC_BOOT_FAILURE;
hidehiko321d423c2016-09-30 15:51:30603 } else {
604 // Otherwise, this is caused by CRASH occured inside of the ARC instance.
hidehikoae82e3a2017-03-01 20:05:39605 reason = ArcStopReason::CRASH;
nya0c9dabf2016-07-13 19:54:27606 }
hidehiko321d423c2016-09-30 15:51:30607 OnStopped(reason);
lhchavezf1c055372015-12-07 22:51:18608}
609
hidehikoae82e3a2017-03-01 20:05:39610void ArcSessionImpl::OnStopped(ArcStopReason reason) {
Hidehiko Abea0cfefae2017-06-22 19:30:11611 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
hidehiko321d423c2016-09-30 15:51:30612 // OnStopped() should be called once per instance.
613 DCHECK_NE(state_, State::STOPPED);
614 VLOG(2) << "ARC session is stopped.";
hidehikoa028d092016-10-05 02:20:23615 arc_bridge_host_.reset();
hidehiko321d423c2016-09-30 15:51:30616 state_ = State::STOPPED;
yusukes5e2934b2016-10-12 19:24:14617 for (auto& observer : observer_list_)
hidehiko9bc9ba52016-12-14 09:01:18618 observer.OnSessionStopped(reason);
lhchavezf1c055372015-12-07 22:51:18619}
620
hidehiko13622872016-10-21 10:05:09621void ArcSessionImpl::OnShutdown() {
Hidehiko Abea0cfefae2017-06-22 19:30:11622 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
hidehiko13622872016-10-21 10:05:09623 stop_requested_ = true;
624 if (state_ == State::STOPPED)
625 return;
626
627 // Here, the message loop is already stopped, and the Chrome will be soon
628 // shutdown. Thus, it is not necessary to take care about restarting case.
629 // If ArcSession is waiting for mojo connection, cancels it. The BlockingPool
630 // will be joined later.
631 accept_cancel_pipe_.reset();
632
633 // Stops the ARC instance to let it graceful shutdown.
634 // Note that this may fail if ARC container is not actually running, but
635 // ignore an error as described below.
636 if (state_ == State::STARTING_INSTANCE ||
637 state_ == State::CONNECTING_MOJO || state_ == State::RUNNING)
638 StopArcInstance();
639
640 // Directly set to the STOPPED stateby OnStopped(). Note that calling
641 // StopArcInstance() may not work well. At least, because the UI thread is
642 // already stopped here, ArcInstanceStopped() callback cannot be invoked.
hidehikoae82e3a2017-03-01 20:05:39643 OnStopped(ArcStopReason::SHUTDOWN);
hidehiko13622872016-10-21 10:05:09644}
645
lhchavezf1c055372015-12-07 22:51:18646} // namespace
647
hidehiko88160c82016-10-18 09:43:47648ArcSession::ArcSession() = default;
649ArcSession::~ArcSession() = default;
hidehikoa028d092016-10-05 02:20:23650
hidehikoae82e3a2017-03-01 20:05:39651void ArcSession::AddObserver(Observer* observer) {
hidehikoa028d092016-10-05 02:20:23652 observer_list_.AddObserver(observer);
653}
654
hidehikoae82e3a2017-03-01 20:05:39655void ArcSession::RemoveObserver(Observer* observer) {
hidehikoa028d092016-10-05 02:20:23656 observer_list_.RemoveObserver(observer);
657}
658
lhchavezf1c055372015-12-07 22:51:18659// static
hidehiko13622872016-10-21 10:05:09660std::unique_ptr<ArcSession> ArcSession::Create(
hidehiko01319c4e2016-12-08 05:59:46661 ArcBridgeService* arc_bridge_service,
hidehiko13622872016-10-21 10:05:09662 const scoped_refptr<base::TaskRunner>& blocking_task_runner) {
hidehiko01319c4e2016-12-08 05:59:46663 return base::MakeUnique<ArcSessionImpl>(arc_bridge_service,
664 blocking_task_runner);
lhchavezf1c055372015-12-07 22:51:18665}
666
667} // namespace arc