Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(616)

Side by Side Diff: components/arc/arc_bridge_bootstrap.cc

Issue 2425753002: Rename ArcBridgeBootstrap to ArcSession. (Closed)
Patch Set: Address comment. Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/arc/arc_bridge_bootstrap.h ('k') | components/arc/arc_bridge_service.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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_bootstrap.h"
6
7 #include <fcntl.h>
8 #include <grp.h>
9 #include <poll.h>
10 #include <unistd.h>
11
12 #include <utility>
13
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/location.h"
17 #include "base/macros.h"
18 #include "base/memory/ptr_util.h"
19 #include "base/posix/eintr_wrapper.h"
20 #include "base/sys_info.h"
21 #include "base/task_runner_util.h"
22 #include "base/threading/thread_checker.h"
23 #include "base/threading/thread_task_runner_handle.h"
24 #include "base/threading/worker_pool.h"
25 #include "chromeos/cryptohome/cryptohome_parameters.h"
26 #include "chromeos/dbus/dbus_method_call_status.h"
27 #include "chromeos/dbus/dbus_thread_manager.h"
28 #include "chromeos/dbus/session_manager_client.h"
29 #include "components/arc/arc_bridge_host_impl.h"
30 #include "components/arc/arc_features.h"
31 #include "components/user_manager/user_manager.h"
32 #include "ipc/unix_domain_socket_util.h"
33 #include "mojo/edk/embedder/embedder.h"
34 #include "mojo/edk/embedder/platform_channel_pair.h"
35 #include "mojo/edk/embedder/platform_channel_utils_posix.h"
36 #include "mojo/edk/embedder/platform_handle_vector.h"
37 #include "mojo/edk/embedder/scoped_platform_handle.h"
38 #include "mojo/public/cpp/bindings/binding.h"
39
40 namespace arc {
41
42 namespace {
43
44 using StartArcInstanceResult =
45 chromeos::SessionManagerClient::StartArcInstanceResult;
46
47 const base::FilePath::CharType kArcBridgeSocketPath[] =
48 FILE_PATH_LITERAL("/var/run/chrome/arc_bridge.sock");
49
50 const char kArcBridgeSocketGroup[] = "arc-bridge";
51
52 // This is called when StopArcInstance D-Bus method completes. Since we have the
53 // ArcInstanceStopped() callback and are notified if StartArcInstance fails, we
54 // don't need to do anything when StopArcInstance completes.
55 void DoNothingInstanceStopped(bool) {}
56
57 chromeos::SessionManagerClient* GetSessionManagerClient() {
58 // If the DBusThreadManager or the SessionManagerClient aren't available,
59 // there isn't much we can do. This should only happen when running tests.
60 if (!chromeos::DBusThreadManager::IsInitialized() ||
61 !chromeos::DBusThreadManager::Get() ||
62 !chromeos::DBusThreadManager::Get()->GetSessionManagerClient())
63 return nullptr;
64 return chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
65 }
66
67 // Creates a pipe. Returns true on success, otherwise false.
68 // On success, |read_fd| will be set to the fd of the read side, and
69 // |write_fd| will be set to the one of write side.
70 bool CreatePipe(base::ScopedFD* read_fd, base::ScopedFD* write_fd) {
71 int fds[2];
72 if (pipe2(fds, O_NONBLOCK | O_CLOEXEC) < 0) {
73 PLOG(ERROR) << "pipe2()";
74 return false;
75 }
76
77 read_fd->reset(fds[0]);
78 write_fd->reset(fds[1]);
79 return true;
80 }
81
82 // Waits until |raw_socket_fd| is readable.
83 // The operation may be cancelled originally triggered by user interaction to
84 // disable ARC, or ARC instance is unexpectedly stopped (e.g. crash).
85 // To notify such a situation, |raw_cancel_fd| is also passed to here, and the
86 // write side will be closed in such a case.
87 bool WaitForSocketReadable(int raw_socket_fd, int raw_cancel_fd) {
88 struct pollfd fds[2] = {
89 {raw_socket_fd, POLLIN, 0}, {raw_cancel_fd, POLLIN, 0},
90 };
91
92 if (HANDLE_EINTR(poll(fds, arraysize(fds), -1)) <= 0) {
93 PLOG(ERROR) << "poll()";
94 return false;
95 }
96
97 if (fds[1].revents) {
98 // Notified that Stop() is invoked. Cancel the Mojo connecting.
99 VLOG(1) << "Stop() was called during ConnectMojo()";
100 return false;
101 }
102
103 DCHECK(fds[0].revents);
104 return true;
105 }
106
107 // TODO(hidehiko): Refactor more to make this class unittest-able, for at least
108 // state-machine part.
109 class ArcBridgeBootstrapImpl : public ArcBridgeBootstrap,
110 public chromeos::SessionManagerClient::Observer {
111 public:
112 // The possible states of the bootstrap connection. In the normal flow,
113 // the state changes in the following sequence:
114 //
115 // NOT_STARTED
116 // Start() ->
117 // CREATING_SOCKET
118 // CreateSocket() -> OnSocketCreated() ->
119 // STARTING_INSTANCE
120 // -> OnInstanceStarted() ->
121 // CONNECTING_MOJO
122 // ConnectMojo() -> OnMojoConnected() ->
123 // RUNNING
124 //
125 // At any state, Stop() can be called. It does not immediately stop the
126 // instance, but will eventually stop it.
127 // The actual stop will be notified via Observer::OnStopped().
128 //
129 // When Stop() is called, it makes various behavior based on the current
130 // phase.
131 //
132 // NOT_STARTED:
133 // Do nothing. Immediately transition to the STOPPED state.
134 // CREATING_SOCKET:
135 // The main task of the phase runs on WorkerPool thread. So, Stop() just
136 // sets the flag and return. On the main task completion, a callback
137 // will run on the main (practically UI) thread, and the flag is checked
138 // at the beginning of them. This should work under the assumption that
139 // the main tasks do not block indefinitely.
140 // STARTING_INSTANCE:
141 // The ARC instance is starting via SessionManager. So, similar to
142 // CREATING_SOCKET case, Stop() just sets the flag and return. In its
143 // callback, it checks if ARC instance is successfully started or not.
144 // In case of success, a request to stop the ARC instance is sent to
145 // SessionManager. Its completion will be notified via ArcInstanceStopped.
146 // Otherwise, it just turns into STOPPED state.
147 // CONNECTING_MOJO:
148 // The main task runs on WorkerPool thread, but it is blocking call.
149 // So, Stop() sends a request to cancel the blocking by closing the pipe
150 // whose read side is also polled. Then, in its callback, similar to
151 // STARTING_INSTANCE, a request to stop the ARC instance is sent to
152 // SessionManager, and ArcInstanceStopped handles remaining procedure.
153 // RUNNING:
154 // There is no more callback which runs on normal flow, so Stop() requests
155 // to stop the ARC instance via SessionManager.
156 //
157 // Another trigger to change the state coming from outside of this class
158 // is an event ArcInstanceStopped() sent from SessionManager, when ARC
159 // instace unexpectedly terminates. ArcInstanceStopped() turns the state into
160 // STOPPED immediately.
161 // This happens only when STARTING_INSTANCE, CONNECTING_MOJO or RUNNING
162 // state.
163 //
164 // STARTING_INSTANCE:
165 // In OnInstanceStarted(), |state_| is checked at the beginning. If it is
166 // STOPPED, then ArcInstanceStopped() is called. Do nothing in that case.
167 // CONNECTING_MOJO:
168 // Similar to Stop() case above, ArcInstanceStopped() also notifies to
169 // WorkerPool() thread to cancel it to unblock the thread. In
170 // OnMojoConnected(), similar to OnInstanceStarted(), check if |state_| is
171 // STOPPED, then do nothing.
172 // RUNNING:
173 // It is not necessary to do anything special here.
174 //
175 // In NOT_STARTED or STOPPED state, the instance can be safely destructed.
176 // Specifically, in STOPPED state, there may be inflight operations or
177 // pending callbacks. Though, what they do is just do-nothing conceptually
178 // and they can be safely ignored.
179 //
180 // Note: Order of constants below matters. Please make sure to sort them
181 // in chronological order.
182 enum class State {
183 // ARC is not yet started.
184 NOT_STARTED,
185
186 // An UNIX socket is being created.
187 CREATING_SOCKET,
188
189 // The request to start the instance has been sent.
190 STARTING_INSTANCE,
191
192 // The instance has started. Waiting for it to connect to the IPC bridge.
193 CONNECTING_MOJO,
194
195 // The instance is fully set up.
196 RUNNING,
197
198 // ARC is terminated.
199 STOPPED,
200 };
201
202 ArcBridgeBootstrapImpl();
203 ~ArcBridgeBootstrapImpl() override;
204
205 // ArcBridgeBootstrap:
206 void Start() override;
207 void Stop() override;
208
209 private:
210 // Creates the UNIX socket on the bootstrap thread and then processes its
211 // file descriptor.
212 static base::ScopedFD CreateSocket();
213 void OnSocketCreated(base::ScopedFD fd);
214
215 // DBus callback for StartArcInstance().
216 void OnInstanceStarted(base::ScopedFD socket_fd,
217 StartArcInstanceResult result);
218
219 // Synchronously accepts a connection on |socket_fd| and then processes the
220 // connected socket's file descriptor.
221 static base::ScopedFD ConnectMojo(base::ScopedFD socket_fd,
222 base::ScopedFD cancel_fd);
223 void OnMojoConnected(base::ScopedFD fd);
224
225 // Request to stop ARC instance via DBus.
226 void StopArcInstance();
227
228 // chromeos::SessionManagerClient::Observer:
229 void ArcInstanceStopped(bool clean) override;
230
231 // Completes the termination procedure.
232 void OnStopped(ArcBridgeService::StopReason reason);
233
234 // The state of the bootstrap connection.
235 State state_ = State::NOT_STARTED;
236
237 // When Stop() is called, this flag is set.
238 bool stop_requested_ = false;
239
240 // In CONNECTING_MOJO state, this is set to the write side of the pipe
241 // to notify cancelling of the procedure.
242 base::ScopedFD accept_cancel_pipe_;
243
244 // Mojo endpoint.
245 std::unique_ptr<mojom::ArcBridgeHost> arc_bridge_host_;
246
247 base::ThreadChecker thread_checker_;
248
249 // WeakPtrFactory to use callbacks.
250 base::WeakPtrFactory<ArcBridgeBootstrapImpl> weak_factory_;
251
252 private:
253 DISALLOW_COPY_AND_ASSIGN(ArcBridgeBootstrapImpl);
254 };
255
256 ArcBridgeBootstrapImpl::ArcBridgeBootstrapImpl()
257 : weak_factory_(this) {
258 chromeos::SessionManagerClient* client = GetSessionManagerClient();
259 if (client == nullptr)
260 return;
261 client->AddObserver(this);
262 }
263
264 ArcBridgeBootstrapImpl::~ArcBridgeBootstrapImpl() {
265 DCHECK(thread_checker_.CalledOnValidThread());
266 // TODO(hidehiko): CHECK if |state_| is in NOT_STARTED or STOPPED.
267 // Currently, specifically on shutdown, the state_ can be any value.
268 chromeos::SessionManagerClient* client = GetSessionManagerClient();
269 if (client == nullptr)
270 return;
271 client->RemoveObserver(this);
272 }
273
274 void ArcBridgeBootstrapImpl::Start() {
275 DCHECK(thread_checker_.CalledOnValidThread());
276 DCHECK_EQ(state_, State::NOT_STARTED);
277 VLOG(2) << "Starting ARC session.";
278 VLOG(2) << "Creating socket...";
279
280 state_ = State::CREATING_SOCKET;
281 base::PostTaskAndReplyWithResult(
282 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE,
283 base::Bind(&ArcBridgeBootstrapImpl::CreateSocket),
284 base::Bind(&ArcBridgeBootstrapImpl::OnSocketCreated,
285 weak_factory_.GetWeakPtr()));
286 }
287
288 // static
289 base::ScopedFD ArcBridgeBootstrapImpl::CreateSocket() {
290 base::FilePath socket_path(kArcBridgeSocketPath);
291
292 int raw_fd = -1;
293 if (!IPC::CreateServerUnixDomainSocket(socket_path, &raw_fd))
294 return base::ScopedFD();
295 base::ScopedFD socket_fd(raw_fd);
296
297 // Change permissions on the socket.
298 struct group arc_bridge_group;
299 struct group* arc_bridge_group_res = nullptr;
300 char buf[10000];
301 if (HANDLE_EINTR(getgrnam_r(kArcBridgeSocketGroup, &arc_bridge_group, buf,
302 sizeof(buf), &arc_bridge_group_res)) < 0) {
303 PLOG(ERROR) << "getgrnam_r";
304 return base::ScopedFD();
305 }
306
307 if (!arc_bridge_group_res) {
308 LOG(ERROR) << "Group '" << kArcBridgeSocketGroup << "' not found";
309 return base::ScopedFD();
310 }
311
312 if (HANDLE_EINTR(chown(kArcBridgeSocketPath, -1, arc_bridge_group.gr_gid)) <
313 0) {
314 PLOG(ERROR) << "chown";
315 return base::ScopedFD();
316 }
317
318 if (!base::SetPosixFilePermissions(socket_path, 0660)) {
319 PLOG(ERROR) << "Could not set permissions: " << socket_path.value();
320 return base::ScopedFD();
321 }
322
323 return socket_fd;
324 }
325
326 void ArcBridgeBootstrapImpl::OnSocketCreated(base::ScopedFD socket_fd) {
327 DCHECK(thread_checker_.CalledOnValidThread());
328 DCHECK_EQ(state_, State::CREATING_SOCKET);
329
330 if (stop_requested_) {
331 VLOG(1) << "Stop() called while connecting";
332 OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
333 return;
334 }
335
336 if (!socket_fd.is_valid()) {
337 LOG(ERROR) << "ARC: Error creating socket";
338 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
339 return;
340 }
341
342 VLOG(2) << "Socket is created. Starting ARC instance...";
343 state_ = State::STARTING_INSTANCE;
344 user_manager::UserManager* user_manager = user_manager::UserManager::Get();
345 DCHECK(user_manager->GetPrimaryUser());
346 const cryptohome::Identification cryptohome_id(
347 user_manager->GetPrimaryUser()->GetAccountId());
348
349 bool disable_boot_completed_broadcast =
350 !base::FeatureList::IsEnabled(arc::kBootCompletedBroadcastFeature);
351
352 chromeos::SessionManagerClient* session_manager_client =
353 chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
354 session_manager_client->StartArcInstance(
355 cryptohome_id,
356 disable_boot_completed_broadcast,
357 base::Bind(&ArcBridgeBootstrapImpl::OnInstanceStarted,
358 weak_factory_.GetWeakPtr(), base::Passed(&socket_fd)));
359 }
360
361 void ArcBridgeBootstrapImpl::OnInstanceStarted(base::ScopedFD socket_fd,
362 StartArcInstanceResult result) {
363 DCHECK(thread_checker_.CalledOnValidThread());
364 if (state_ == State::STOPPED) {
365 // This is the case that error is notified via DBus before the
366 // OnInstanceStarted() callback is invoked. The stopping procedure has
367 // been run, so do nothing.
368 return;
369 }
370
371 DCHECK_EQ(state_, State::STARTING_INSTANCE);
372
373 if (stop_requested_) {
374 if (result == StartArcInstanceResult::SUCCESS) {
375 // The ARC instance has started to run. Request to stop.
376 StopArcInstance();
377 return;
378 }
379 OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
380 return;
381 }
382
383 if (result != StartArcInstanceResult::SUCCESS) {
384 LOG(ERROR) << "Failed to start ARC instance";
385 OnStopped(result == StartArcInstanceResult::LOW_FREE_DISK_SPACE
386 ? ArcBridgeService::StopReason::LOW_DISK_SPACE
387 : ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
388 return;
389 }
390
391 VLOG(2) << "ARC instance is successfully started. Connecting Mojo...";
392 state_ = State::CONNECTING_MOJO;
393
394 // Prepare a pipe so that AcceptInstanceConnection can be interrupted on
395 // Stop().
396 base::ScopedFD cancel_fd;
397 if (!CreatePipe(&cancel_fd, &accept_cancel_pipe_)) {
398 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
399 return;
400 }
401
402 base::PostTaskAndReplyWithResult(
403 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE,
404 base::Bind(&ArcBridgeBootstrapImpl::ConnectMojo, base::Passed(&socket_fd),
405 base::Passed(&cancel_fd)),
406 base::Bind(&ArcBridgeBootstrapImpl::OnMojoConnected,
407 weak_factory_.GetWeakPtr()));
408 }
409
410 // static
411 base::ScopedFD ArcBridgeBootstrapImpl::ConnectMojo(base::ScopedFD socket_fd,
412 base::ScopedFD cancel_fd) {
413 if (!WaitForSocketReadable(socket_fd.get(), cancel_fd.get())) {
414 VLOG(1) << "Mojo connection was cancelled.";
415 return base::ScopedFD();
416 }
417
418 int raw_fd = -1;
419 if (!IPC::ServerOnConnect(socket_fd.get(), &raw_fd)) {
420 return base::ScopedFD();
421 }
422 base::ScopedFD scoped_fd(raw_fd);
423
424 // Hardcode pid 0 since it is unused in mojo.
425 const base::ProcessHandle kUnusedChildProcessHandle = 0;
426 mojo::edk::PlatformChannelPair channel_pair;
427 mojo::edk::ChildProcessLaunched(kUnusedChildProcessHandle,
428 channel_pair.PassServerHandle(),
429 mojo::edk::GenerateRandomToken());
430
431 mojo::edk::ScopedPlatformHandleVectorPtr handles(
432 new mojo::edk::PlatformHandleVector{
433 channel_pair.PassClientHandle().release()});
434
435 struct iovec iov = {const_cast<char*>(""), 1};
436 ssize_t result = mojo::edk::PlatformChannelSendmsgWithHandles(
437 mojo::edk::PlatformHandle(scoped_fd.get()), &iov, 1, handles->data(),
438 handles->size());
439 if (result == -1) {
440 PLOG(ERROR) << "sendmsg";
441 return base::ScopedFD();
442 }
443
444 return scoped_fd;
445 }
446
447 void ArcBridgeBootstrapImpl::OnMojoConnected(base::ScopedFD fd) {
448 DCHECK(thread_checker_.CalledOnValidThread());
449
450 if (state_ == State::STOPPED) {
451 // This is the case that error is notified via DBus before the
452 // OnMojoConnected() callback is invoked. The stopping procedure has
453 // been run, so do nothing.
454 return;
455 }
456
457 DCHECK_EQ(state_, State::CONNECTING_MOJO);
458 accept_cancel_pipe_.reset();
459
460 if (stop_requested_) {
461 StopArcInstance();
462 return;
463 }
464
465 if (!fd.is_valid()) {
466 LOG(ERROR) << "Invalid handle";
467 StopArcInstance();
468 return;
469 }
470
471 mojo::ScopedMessagePipeHandle server_pipe = mojo::edk::CreateMessagePipe(
472 mojo::edk::ScopedPlatformHandle(mojo::edk::PlatformHandle(fd.release())));
473 if (!server_pipe.is_valid()) {
474 LOG(ERROR) << "Invalid pipe";
475 StopArcInstance();
476 return;
477 }
478
479 mojom::ArcBridgeInstancePtr instance;
480 instance.Bind(mojo::InterfacePtrInfo<mojom::ArcBridgeInstance>(
481 std::move(server_pipe), 0u));
482 arc_bridge_host_.reset(new ArcBridgeHostImpl(std::move(instance)));
483
484 VLOG(2) << "Mojo is connected. ARC is running.";
485 state_ = State::RUNNING;
486 for (auto& observer : observer_list_)
487 observer.OnReady();
488 }
489
490 void ArcBridgeBootstrapImpl::Stop() {
491 DCHECK(thread_checker_.CalledOnValidThread());
492 VLOG(2) << "Stopping ARC session is requested.";
493
494 // For second time or later, just do nothing.
495 // It is already in the stopping phase.
496 if (stop_requested_)
497 return;
498
499 stop_requested_ = true;
500 arc_bridge_host_.reset();
501 switch (state_) {
502 case State::NOT_STARTED:
503 OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
504 return;
505
506 case State::CREATING_SOCKET:
507 case State::STARTING_INSTANCE:
508 // Before starting the ARC instance, we do nothing here.
509 // At some point, a callback will be invoked on UI thread,
510 // and stopping procedure will be run there.
511 // On Chrome shutdown, it is not the case because the message loop is
512 // already stopped here. Practically, it is not a problem because;
513 // - On socket creating, it is ok to simply ignore such cases,
514 // because we no-longer continue the bootstrap procedure.
515 // - On starting instance, the container instance can be leaked.
516 // Practically it is not problematic because the session manager will
517 // clean it up.
518 return;
519
520 case State::CONNECTING_MOJO:
521 // Mojo connection is being waited on a WorkerPool thread.
522 // Request to cancel it. Following stopping procedure will run
523 // in its callback.
524 DCHECK(accept_cancel_pipe_.get());
525 accept_cancel_pipe_.reset();
526 return;
527
528 case State::RUNNING:
529 // Now ARC instance is running. Request to stop it.
530 StopArcInstance();
531 return;
532
533 case State::STOPPED:
534 // The instance is already stopped. Do nothing.
535 return;
536 }
537 }
538
539 void ArcBridgeBootstrapImpl::StopArcInstance() {
540 DCHECK(thread_checker_.CalledOnValidThread());
541 DCHECK(state_ == State::STARTING_INSTANCE ||
542 state_ == State::CONNECTING_MOJO || state_ == State::RUNNING);
543
544 // Notification will arrive through ArcInstanceStopped().
545 VLOG(2) << "Requesting to stop ARC instance";
546 chromeos::SessionManagerClient* session_manager_client =
547 chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
548 session_manager_client->StopArcInstance(
549 base::Bind(&DoNothingInstanceStopped));
550 }
551
552 void ArcBridgeBootstrapImpl::ArcInstanceStopped(bool clean) {
553 DCHECK(thread_checker_.CalledOnValidThread());
554 VLOG(1) << "Notified that ARC instance is stopped "
555 << (clean ? "cleanly" : "uncleanly");
556
557 // In case that crash happens during before the Mojo channel is connected,
558 // unlock the WorkerPool thread.
559 accept_cancel_pipe_.reset();
560
561 ArcBridgeService::StopReason reason;
562 if (stop_requested_) {
563 // If the ARC instance is stopped after its explicit request,
564 // return SHUTDOWN.
565 reason = ArcBridgeService::StopReason::SHUTDOWN;
566 } else if (clean) {
567 // If the ARC instance is stopped, but it is not explicitly requested,
568 // then this is triggered by some failure during the starting procedure.
569 // Return GENERIC_BOOT_FAILURE for the case.
570 reason = ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE;
571 } else {
572 // Otherwise, this is caused by CRASH occured inside of the ARC instance.
573 reason = ArcBridgeService::StopReason::CRASH;
574 }
575 OnStopped(reason);
576 }
577
578 void ArcBridgeBootstrapImpl::OnStopped(ArcBridgeService::StopReason reason) {
579 DCHECK(thread_checker_.CalledOnValidThread());
580 // OnStopped() should be called once per instance.
581 DCHECK_NE(state_, State::STOPPED);
582 VLOG(2) << "ARC session is stopped.";
583 arc_bridge_host_.reset();
584 state_ = State::STOPPED;
585 for (auto& observer : observer_list_)
586 observer.OnStopped(reason);
587 }
588
589 } // namespace
590
591 ArcBridgeBootstrap::ArcBridgeBootstrap() = default;
592 ArcBridgeBootstrap::~ArcBridgeBootstrap() = default;
593
594 void ArcBridgeBootstrap::AddObserver(Observer* observer) {
595 observer_list_.AddObserver(observer);
596 }
597
598 void ArcBridgeBootstrap::RemoveObserver(Observer* observer) {
599 observer_list_.RemoveObserver(observer);
600 }
601
602 // static
603 std::unique_ptr<ArcBridgeBootstrap> ArcBridgeBootstrap::Create() {
604 return base::MakeUnique<ArcBridgeBootstrapImpl>();
605 }
606
607 } // namespace arc
OLDNEW
« no previous file with comments | « components/arc/arc_bridge_bootstrap.h ('k') | components/arc/arc_bridge_service.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698