| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/arc/arc_bridge_bootstrap.h" | 5 #include "components/arc/arc_bridge_bootstrap.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <grp.h> | 8 #include <grp.h> |
| 9 #include <poll.h> | 9 #include <poll.h> |
| 10 #include <unistd.h> | 10 #include <unistd.h> |
| (...skipping 23 matching lines...) Expand all Loading... |
| 34 #include "mojo/edk/embedder/platform_channel_pair.h" | 34 #include "mojo/edk/embedder/platform_channel_pair.h" |
| 35 #include "mojo/edk/embedder/platform_channel_utils_posix.h" | 35 #include "mojo/edk/embedder/platform_channel_utils_posix.h" |
| 36 #include "mojo/edk/embedder/platform_handle_vector.h" | 36 #include "mojo/edk/embedder/platform_handle_vector.h" |
| 37 #include "mojo/edk/embedder/scoped_platform_handle.h" | 37 #include "mojo/edk/embedder/scoped_platform_handle.h" |
| 38 #include "mojo/public/cpp/bindings/binding.h" | 38 #include "mojo/public/cpp/bindings/binding.h" |
| 39 | 39 |
| 40 namespace arc { | 40 namespace arc { |
| 41 | 41 |
| 42 namespace { | 42 namespace { |
| 43 | 43 |
| 44 using StartArcInstanceResult = |
| 45 chromeos::SessionManagerClient::StartArcInstanceResult; |
| 46 |
| 44 const base::FilePath::CharType kArcBridgeSocketPath[] = | 47 const base::FilePath::CharType kArcBridgeSocketPath[] = |
| 45 FILE_PATH_LITERAL("/var/run/chrome/arc_bridge.sock"); | 48 FILE_PATH_LITERAL("/var/run/chrome/arc_bridge.sock"); |
| 46 | 49 |
| 47 const char kArcBridgeSocketGroup[] = "arc-bridge"; | 50 const char kArcBridgeSocketGroup[] = "arc-bridge"; |
| 48 | 51 |
| 49 const base::FilePath::CharType kDiskCheckPath[] = "/home"; | |
| 50 | |
| 51 const int64_t kCriticalDiskFreeBytes = 64 << 20; // 64MB | |
| 52 | |
| 53 // This is called when StopArcInstance D-Bus method completes. Since we have the | 52 // This is called when StopArcInstance D-Bus method completes. Since we have the |
| 54 // ArcInstanceStopped() callback and are notified if StartArcInstance fails, we | 53 // ArcInstanceStopped() callback and are notified if StartArcInstance fails, we |
| 55 // don't need to do anything when StopArcInstance completes. | 54 // don't need to do anything when StopArcInstance completes. |
| 56 void DoNothingInstanceStopped(bool) {} | 55 void DoNothingInstanceStopped(bool) {} |
| 57 | 56 |
| 58 chromeos::SessionManagerClient* GetSessionManagerClient() { | 57 chromeos::SessionManagerClient* GetSessionManagerClient() { |
| 59 // If the DBusThreadManager or the SessionManagerClient aren't available, | 58 // If the DBusThreadManager or the SessionManagerClient aren't available, |
| 60 // there isn't much we can do. This should only happen when running tests. | 59 // there isn't much we can do. This should only happen when running tests. |
| 61 if (!chromeos::DBusThreadManager::IsInitialized() || | 60 if (!chromeos::DBusThreadManager::IsInitialized() || |
| 62 !chromeos::DBusThreadManager::Get() || | 61 !chromeos::DBusThreadManager::Get() || |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 108 // TODO(hidehiko): Refactor more to make this class unittest-able, for at least | 107 // TODO(hidehiko): Refactor more to make this class unittest-able, for at least |
| 109 // state-machine part. | 108 // state-machine part. |
| 110 class ArcBridgeBootstrapImpl : public ArcBridgeBootstrap, | 109 class ArcBridgeBootstrapImpl : public ArcBridgeBootstrap, |
| 111 public chromeos::SessionManagerClient::Observer { | 110 public chromeos::SessionManagerClient::Observer { |
| 112 public: | 111 public: |
| 113 // The possible states of the bootstrap connection. In the normal flow, | 112 // The possible states of the bootstrap connection. In the normal flow, |
| 114 // the state changes in the following sequence: | 113 // the state changes in the following sequence: |
| 115 // | 114 // |
| 116 // NOT_STARTED | 115 // NOT_STARTED |
| 117 // Start() -> | 116 // Start() -> |
| 118 // CHECKING_DISK_SPACE | |
| 119 // OnDiskSpaceChecked() -> | |
| 120 // CREATING_SOCKET | 117 // CREATING_SOCKET |
| 121 // CreateSocket() -> OnSocketCreated() -> | 118 // CreateSocket() -> OnSocketCreated() -> |
| 122 // STARTING_INSTANCE | 119 // STARTING_INSTANCE |
| 123 // -> OnInstanceStarted() -> | 120 // -> OnInstanceStarted() -> |
| 124 // CONNECTING_MOJO | 121 // CONNECTING_MOJO |
| 125 // ConnectMojo() -> OnMojoConnected() -> | 122 // ConnectMojo() -> OnMojoConnected() -> |
| 126 // RUNNING | 123 // RUNNING |
| 127 // | 124 // |
| 128 // At any state, Stop() can be called. It does not immediately stop the | 125 // At any state, Stop() can be called. It does not immediately stop the |
| 129 // instance, but will eventually stop it. | 126 // instance, but will eventually stop it. |
| 130 // The actual stop will be notified via Observer::OnStopped(). | 127 // The actual stop will be notified via Observer::OnStopped(). |
| 131 // | 128 // |
| 132 // When Stop() is called, it makes various behavior based on the current | 129 // When Stop() is called, it makes various behavior based on the current |
| 133 // phase. | 130 // phase. |
| 134 // | 131 // |
| 135 // NOT_STARTED: | 132 // NOT_STARTED: |
| 136 // Do nothing. Immediately transition to the STOPPED state. | 133 // Do nothing. Immediately transition to the STOPPED state. |
| 137 // CHECKING_DISK_SPACE, CREATING_SOCKET: | 134 // CREATING_SOCKET: |
| 138 // The main task of those phases runs on WorkerPool thread. So, Stop() | 135 // The main task of the phase runs on WorkerPool thread. So, Stop() just |
| 139 // just sets the flag and return. On the main task completion, a callback | 136 // sets the flag and return. On the main task completion, a callback |
| 140 // will run on the main (practically UI) thread, and the flag is checked | 137 // 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 | 138 // at the beginning of them. This should work under the assumption that |
| 142 // the main tasks do not block indefinitely. | 139 // the main tasks do not block indefinitely. |
| 143 // STARTING_INSTANCE: | 140 // STARTING_INSTANCE: |
| 144 // The ARC instance is starting via SessionManager. So, similar to | 141 // The ARC instance is starting via SessionManager. So, similar to |
| 145 // CHECKING_DISK_SPACE/CREATING_SOCKET cases, Stop() just sets the flag | 142 // CREATING_SOCKET case, Stop() just sets the flag and return. In its |
| 146 // and return. In its callback, it checks if ARC instance is successfully | 143 // callback, it checks if ARC instance is successfully started or not. |
| 147 // started or not. In case of success, a request to stop the ARC instance | 144 // In case of success, a request to stop the ARC instance is sent to |
| 148 // is sent to SessionManager. Its completion will be notified via | 145 // SessionManager. Its completion will be notified via ArcInstanceStopped. |
| 149 // ArcInstanceStopped. Otherwise, it just turns into STOPPED state. | 146 // Otherwise, it just turns into STOPPED state. |
| 150 // CONNECTING_MOJO: | 147 // CONNECTING_MOJO: |
| 151 // The main task runs on WorkerPool thread, but it is blocking call. | 148 // The main task runs on WorkerPool thread, but it is blocking call. |
| 152 // So, Stop() sends a request to cancel the blocking by closing the pipe | 149 // 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 | 150 // 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 | 151 // STARTING_INSTANCE, a request to stop the ARC instance is sent to |
| 155 // SessionManager, and ArcInstanceStopped handles remaining procedure. | 152 // SessionManager, and ArcInstanceStopped handles remaining procedure. |
| 156 // RUNNING: | 153 // RUNNING: |
| 157 // There is no more callback which runs on normal flow, so Stop() requests | 154 // There is no more callback which runs on normal flow, so Stop() requests |
| 158 // to stop the ARC instance via SessionManager. | 155 // to stop the ARC instance via SessionManager. |
| 159 // | 156 // |
| (...skipping 19 matching lines...) Expand all Loading... |
| 179 // Specifically, in STOPPED state, there may be inflight operations or | 176 // Specifically, in STOPPED state, there may be inflight operations or |
| 180 // pending callbacks. Though, what they do is just do-nothing conceptually | 177 // pending callbacks. Though, what they do is just do-nothing conceptually |
| 181 // and they can be safely ignored. | 178 // and they can be safely ignored. |
| 182 // | 179 // |
| 183 // Note: Order of constants below matters. Please make sure to sort them | 180 // Note: Order of constants below matters. Please make sure to sort them |
| 184 // in chronological order. | 181 // in chronological order. |
| 185 enum class State { | 182 enum class State { |
| 186 // ARC is not yet started. | 183 // ARC is not yet started. |
| 187 NOT_STARTED, | 184 NOT_STARTED, |
| 188 | 185 |
| 189 // Checking the disk space. | |
| 190 CHECKING_DISK_SPACE, | |
| 191 | |
| 192 // An UNIX socket is being created. | 186 // An UNIX socket is being created. |
| 193 CREATING_SOCKET, | 187 CREATING_SOCKET, |
| 194 | 188 |
| 195 // The request to start the instance has been sent. | 189 // The request to start the instance has been sent. |
| 196 STARTING_INSTANCE, | 190 STARTING_INSTANCE, |
| 197 | 191 |
| 198 // The instance has started. Waiting for it to connect to the IPC bridge. | 192 // The instance has started. Waiting for it to connect to the IPC bridge. |
| 199 CONNECTING_MOJO, | 193 CONNECTING_MOJO, |
| 200 | 194 |
| 201 // The instance is fully set up. | 195 // The instance is fully set up. |
| 202 RUNNING, | 196 RUNNING, |
| 203 | 197 |
| 204 // ARC is terminated. | 198 // ARC is terminated. |
| 205 STOPPED, | 199 STOPPED, |
| 206 }; | 200 }; |
| 207 | 201 |
| 208 ArcBridgeBootstrapImpl(); | 202 ArcBridgeBootstrapImpl(); |
| 209 ~ArcBridgeBootstrapImpl() override; | 203 ~ArcBridgeBootstrapImpl() override; |
| 210 | 204 |
| 211 // ArcBridgeBootstrap: | 205 // ArcBridgeBootstrap: |
| 212 void Start() override; | 206 void Start() override; |
| 213 void Stop() override; | 207 void Stop() override; |
| 214 | 208 |
| 215 private: | 209 private: |
| 216 // Called after getting the device free disk space. | |
| 217 void OnFreeDiskSpaceObtained(int64_t disk_free_bytes); | |
| 218 | |
| 219 // Creates the UNIX socket on the bootstrap thread and then processes its | 210 // Creates the UNIX socket on the bootstrap thread and then processes its |
| 220 // file descriptor. | 211 // file descriptor. |
| 221 static base::ScopedFD CreateSocket(); | 212 static base::ScopedFD CreateSocket(); |
| 222 void OnSocketCreated(base::ScopedFD fd); | 213 void OnSocketCreated(base::ScopedFD fd); |
| 223 | 214 |
| 224 // DBus callback for StartArcInstance(). | 215 // DBus callback for StartArcInstance(). |
| 225 void OnInstanceStarted(base::ScopedFD socket_fd, bool success); | 216 void OnInstanceStarted(base::ScopedFD socket_fd, |
| 217 StartArcInstanceResult result); |
| 226 | 218 |
| 227 // Synchronously accepts a connection on |socket_fd| and then processes the | 219 // Synchronously accepts a connection on |socket_fd| and then processes the |
| 228 // connected socket's file descriptor. | 220 // connected socket's file descriptor. |
| 229 static base::ScopedFD ConnectMojo(base::ScopedFD socket_fd, | 221 static base::ScopedFD ConnectMojo(base::ScopedFD socket_fd, |
| 230 base::ScopedFD cancel_fd); | 222 base::ScopedFD cancel_fd); |
| 231 void OnMojoConnected(base::ScopedFD fd); | 223 void OnMojoConnected(base::ScopedFD fd); |
| 232 | 224 |
| 233 // Request to stop ARC instance via DBus. | 225 // Request to stop ARC instance via DBus. |
| 234 void StopArcInstance(); | 226 void StopArcInstance(); |
| 235 | 227 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 276 chromeos::SessionManagerClient* client = GetSessionManagerClient(); | 268 chromeos::SessionManagerClient* client = GetSessionManagerClient(); |
| 277 if (client == nullptr) | 269 if (client == nullptr) |
| 278 return; | 270 return; |
| 279 client->RemoveObserver(this); | 271 client->RemoveObserver(this); |
| 280 } | 272 } |
| 281 | 273 |
| 282 void ArcBridgeBootstrapImpl::Start() { | 274 void ArcBridgeBootstrapImpl::Start() { |
| 283 DCHECK(thread_checker_.CalledOnValidThread()); | 275 DCHECK(thread_checker_.CalledOnValidThread()); |
| 284 DCHECK_EQ(state_, State::NOT_STARTED); | 276 DCHECK_EQ(state_, State::NOT_STARTED); |
| 285 VLOG(2) << "Starting ARC session."; | 277 VLOG(2) << "Starting ARC session."; |
| 286 VLOG(2) << "Checking disk space..."; | 278 VLOG(2) << "Creating socket..."; |
| 287 state_ = State::CHECKING_DISK_SPACE; | |
| 288 | 279 |
| 289 // TODO(crbug.com/628124): Move disk space checking logic to session_manager. | |
| 290 base::PostTaskAndReplyWithResult( | |
| 291 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE, | |
| 292 base::Bind(&base::SysInfo::AmountOfFreeDiskSpace, | |
| 293 base::FilePath(kDiskCheckPath)), | |
| 294 base::Bind(&ArcBridgeBootstrapImpl::OnFreeDiskSpaceObtained, | |
| 295 weak_factory_.GetWeakPtr())); | |
| 296 } | |
| 297 | |
| 298 void ArcBridgeBootstrapImpl::OnFreeDiskSpaceObtained(int64_t disk_free_bytes) { | |
| 299 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 300 DCHECK_EQ(state_, State::CHECKING_DISK_SPACE); | |
| 301 | |
| 302 if (stop_requested_) { | |
| 303 VLOG(1) << "Stop() called while checking disk space"; | |
| 304 OnStopped(ArcBridgeService::StopReason::SHUTDOWN); | |
| 305 return; | |
| 306 } | |
| 307 | |
| 308 if (disk_free_bytes < 0) { | |
| 309 LOG(ERROR) << "ARC: Failed to get free disk space"; | |
| 310 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); | |
| 311 return; | |
| 312 } | |
| 313 if (disk_free_bytes < kCriticalDiskFreeBytes) { | |
| 314 LOG(ERROR) << "ARC: The device is too low on disk space to start ARC"; | |
| 315 OnStopped(ArcBridgeService::StopReason::LOW_DISK_SPACE); | |
| 316 return; | |
| 317 } | |
| 318 | |
| 319 VLOG(2) << "Disk space check is done. Creating socket..."; | |
| 320 state_ = State::CREATING_SOCKET; | 280 state_ = State::CREATING_SOCKET; |
| 321 base::PostTaskAndReplyWithResult( | 281 base::PostTaskAndReplyWithResult( |
| 322 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE, | 282 base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE, |
| 323 base::Bind(&ArcBridgeBootstrapImpl::CreateSocket), | 283 base::Bind(&ArcBridgeBootstrapImpl::CreateSocket), |
| 324 base::Bind(&ArcBridgeBootstrapImpl::OnSocketCreated, | 284 base::Bind(&ArcBridgeBootstrapImpl::OnSocketCreated, |
| 325 weak_factory_.GetWeakPtr())); | 285 weak_factory_.GetWeakPtr())); |
| 326 } | 286 } |
| 327 | 287 |
| 328 // static | 288 // static |
| 329 base::ScopedFD ArcBridgeBootstrapImpl::CreateSocket() { | 289 base::ScopedFD ArcBridgeBootstrapImpl::CreateSocket() { |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 392 chromeos::SessionManagerClient* session_manager_client = | 352 chromeos::SessionManagerClient* session_manager_client = |
| 393 chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); | 353 chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); |
| 394 session_manager_client->StartArcInstance( | 354 session_manager_client->StartArcInstance( |
| 395 cryptohome_id, | 355 cryptohome_id, |
| 396 disable_boot_completed_broadcast, | 356 disable_boot_completed_broadcast, |
| 397 base::Bind(&ArcBridgeBootstrapImpl::OnInstanceStarted, | 357 base::Bind(&ArcBridgeBootstrapImpl::OnInstanceStarted, |
| 398 weak_factory_.GetWeakPtr(), base::Passed(&socket_fd))); | 358 weak_factory_.GetWeakPtr(), base::Passed(&socket_fd))); |
| 399 } | 359 } |
| 400 | 360 |
| 401 void ArcBridgeBootstrapImpl::OnInstanceStarted(base::ScopedFD socket_fd, | 361 void ArcBridgeBootstrapImpl::OnInstanceStarted(base::ScopedFD socket_fd, |
| 402 bool success) { | 362 StartArcInstanceResult result) { |
| 403 DCHECK(thread_checker_.CalledOnValidThread()); | 363 DCHECK(thread_checker_.CalledOnValidThread()); |
| 404 if (state_ == State::STOPPED) { | 364 if (state_ == State::STOPPED) { |
| 405 // This is the case that error is notified via DBus before the | 365 // This is the case that error is notified via DBus before the |
| 406 // OnInstanceStarted() callback is invoked. The stopping procedure has | 366 // OnInstanceStarted() callback is invoked. The stopping procedure has |
| 407 // been run, so do nothing. | 367 // been run, so do nothing. |
| 408 return; | 368 return; |
| 409 } | 369 } |
| 410 | 370 |
| 411 DCHECK_EQ(state_, State::STARTING_INSTANCE); | 371 DCHECK_EQ(state_, State::STARTING_INSTANCE); |
| 412 | 372 |
| 413 if (stop_requested_) { | 373 if (stop_requested_) { |
| 414 if (success) { | 374 if (result == StartArcInstanceResult::SUCCESS) { |
| 415 // The ARC instance has started to run. Request to stop. | 375 // The ARC instance has started to run. Request to stop. |
| 416 StopArcInstance(); | 376 StopArcInstance(); |
| 417 return; | 377 return; |
| 418 } | 378 } |
| 419 OnStopped(ArcBridgeService::StopReason::SHUTDOWN); | 379 OnStopped(ArcBridgeService::StopReason::SHUTDOWN); |
| 420 return; | 380 return; |
| 421 } | 381 } |
| 422 | 382 |
| 423 if (!success) { | 383 if (result != StartArcInstanceResult::SUCCESS) { |
| 424 LOG(ERROR) << "Failed to start ARC instance"; | 384 LOG(ERROR) << "Failed to start ARC instance"; |
| 425 OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); | 385 OnStopped(result == StartArcInstanceResult::LOW_FREE_DISK_SPACE |
| 386 ? ArcBridgeService::StopReason::LOW_DISK_SPACE |
| 387 : ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE); |
| 426 return; | 388 return; |
| 427 } | 389 } |
| 428 | 390 |
| 429 VLOG(2) << "ARC instance is successfully started. Connecting Mojo..."; | 391 VLOG(2) << "ARC instance is successfully started. Connecting Mojo..."; |
| 430 state_ = State::CONNECTING_MOJO; | 392 state_ = State::CONNECTING_MOJO; |
| 431 | 393 |
| 432 // Prepare a pipe so that AcceptInstanceConnection can be interrupted on | 394 // Prepare a pipe so that AcceptInstanceConnection can be interrupted on |
| 433 // Stop(). | 395 // Stop(). |
| 434 base::ScopedFD cancel_fd; | 396 base::ScopedFD cancel_fd; |
| 435 if (!CreatePipe(&cancel_fd, &accept_cancel_pipe_)) { | 397 if (!CreatePipe(&cancel_fd, &accept_cancel_pipe_)) { |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 533 if (stop_requested_) | 495 if (stop_requested_) |
| 534 return; | 496 return; |
| 535 | 497 |
| 536 stop_requested_ = true; | 498 stop_requested_ = true; |
| 537 arc_bridge_host_.reset(); | 499 arc_bridge_host_.reset(); |
| 538 switch (state_) { | 500 switch (state_) { |
| 539 case State::NOT_STARTED: | 501 case State::NOT_STARTED: |
| 540 OnStopped(ArcBridgeService::StopReason::SHUTDOWN); | 502 OnStopped(ArcBridgeService::StopReason::SHUTDOWN); |
| 541 return; | 503 return; |
| 542 | 504 |
| 543 case State::CHECKING_DISK_SPACE: | |
| 544 case State::CREATING_SOCKET: | 505 case State::CREATING_SOCKET: |
| 545 case State::STARTING_INSTANCE: | 506 case State::STARTING_INSTANCE: |
| 546 // Before starting the ARC instance, we do nothing here. | 507 // Before starting the ARC instance, we do nothing here. |
| 547 // At some point, a callback will be invoked on UI thread, | 508 // At some point, a callback will be invoked on UI thread, |
| 548 // and stopping procedure will be run there. | 509 // and stopping procedure will be run there. |
| 549 // On Chrome shutdown, it is not the case because the message loop is | 510 // On Chrome shutdown, it is not the case because the message loop is |
| 550 // already stopped here. Practically, it is not a problem because; | 511 // already stopped here. Practically, it is not a problem because; |
| 551 // - On disk space checking or on socket creating, it is ok to simply | 512 // - On socket creating, it is ok to simply ignore such cases, |
| 552 // ignore such cases, because we no-longer continue the bootstrap | 513 // because we no-longer continue the bootstrap procedure. |
| 553 // procedure. | |
| 554 // - On starting instance, the container instance can be leaked. | 514 // - On starting instance, the container instance can be leaked. |
| 555 // Practically it is not problematic because the session manager will | 515 // Practically it is not problematic because the session manager will |
| 556 // clean it up. | 516 // clean it up. |
| 557 return; | 517 return; |
| 558 | 518 |
| 559 case State::CONNECTING_MOJO: | 519 case State::CONNECTING_MOJO: |
| 560 // Mojo connection is being waited on a WorkerPool thread. | 520 // Mojo connection is being waited on a WorkerPool thread. |
| 561 // Request to cancel it. Following stopping procedure will run | 521 // Request to cancel it. Following stopping procedure will run |
| 562 // in its callback. | 522 // in its callback. |
| 563 DCHECK(accept_cancel_pipe_.get()); | 523 DCHECK(accept_cancel_pipe_.get()); |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 637 void ArcBridgeBootstrap::RemoveObserver(Observer* observer) { | 597 void ArcBridgeBootstrap::RemoveObserver(Observer* observer) { |
| 638 observer_list_.RemoveObserver(observer); | 598 observer_list_.RemoveObserver(observer); |
| 639 } | 599 } |
| 640 | 600 |
| 641 // static | 601 // static |
| 642 std::unique_ptr<ArcBridgeBootstrap> ArcBridgeBootstrap::Create() { | 602 std::unique_ptr<ArcBridgeBootstrap> ArcBridgeBootstrap::Create() { |
| 643 return base::MakeUnique<ArcBridgeBootstrapImpl>(); | 603 return base::MakeUnique<ArcBridgeBootstrapImpl>(); |
| 644 } | 604 } |
| 645 | 605 |
| 646 } // namespace arc | 606 } // namespace arc |
| OLD | NEW |