lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 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 | |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 5 | #include "components/arc/arc_session.h" |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 6 | |
| 7 | #include <fcntl.h> |
lhchavez | d896c6d | 2016-02-23 19:45:42 | [diff] [blame] | 8 | #include <grp.h> |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 9 | #include <poll.h> |
lhchavez | d896c6d | 2016-02-23 19:45:42 | [diff] [blame] | 10 | #include <unistd.h> |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 11 | |
lhchavez | 50abf80a | 2017-01-25 01:04:53 | [diff] [blame] | 12 | #include <string> |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 13 | #include <utility> |
| 14 | |
| 15 | #include "base/files/file_path.h" |
| 16 | #include "base/files/file_util.h" |
| 17 | #include "base/location.h" |
avi | bc5337b | 2015-12-25 23:16:33 | [diff] [blame] | 18 | #include "base/macros.h" |
dcheng | a0ee5fb8 | 2016-04-26 02:46:55 | [diff] [blame] | 19 | #include "base/memory/ptr_util.h" |
lhchavez | 47e4727 | 2016-02-01 21:09:20 | [diff] [blame] | 20 | #include "base/posix/eintr_wrapper.h" |
nya | 1821ddc | 2016-07-19 05:19:39 | [diff] [blame] | 21 | #include "base/sys_info.h" |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 22 | #include "base/task_runner_util.h" |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 23 | #include "base/threading/thread_checker.h" |
gab | 7966d31 | 2016-05-11 20:35:01 | [diff] [blame] | 24 | #include "base/threading/thread_task_runner_handle.h" |
xiaohuic | 6bc1dc0 | 2017-05-08 17:52:01 | [diff] [blame] | 25 | #include "chromeos/chromeos_switches.h" |
nya | 73ba213 | 2016-06-07 05:21:28 | [diff] [blame] | 26 | #include "chromeos/cryptohome/cryptohome_parameters.h" |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 27 | #include "chromeos/dbus/dbus_method_call_status.h" |
| 28 | #include "chromeos/dbus/dbus_thread_manager.h" |
| 29 | #include "chromeos/dbus/session_manager_client.h" |
hidehiko | a028d09 | 2016-10-05 02:20:23 | [diff] [blame] | 30 | #include "components/arc/arc_bridge_host_impl.h" |
xzhou | 938368c | 2016-10-04 04:33:17 | [diff] [blame] | 31 | #include "components/arc/arc_features.h" |
nya | 73ba213 | 2016-06-07 05:21:28 | [diff] [blame] | 32 | #include "components/user_manager/user_manager.h" |
lhchavez | 47e4727 | 2016-02-01 21:09:20 | [diff] [blame] | 33 | #include "mojo/edk/embedder/embedder.h" |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 34 | #include "mojo/edk/embedder/named_platform_handle.h" |
| 35 | #include "mojo/edk/embedder/named_platform_handle_utils.h" |
Ken Rockot | ea1716a0 | 2017-05-11 05:49:10 | [diff] [blame] | 36 | #include "mojo/edk/embedder/outgoing_broker_client_invitation.h" |
lhchavez | 47e4727 | 2016-02-01 21:09:20 | [diff] [blame] | 37 | #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" |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 41 | #include "mojo/public/cpp/bindings/binding.h" |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 42 | |
| 43 | namespace arc { |
| 44 | |
| 45 | namespace { |
| 46 | |
hidehiko | 14f70fe | 2016-10-07 16:13:54 | [diff] [blame] | 47 | using StartArcInstanceResult = |
| 48 | chromeos::SessionManagerClient::StartArcInstanceResult; |
| 49 | |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 50 | const base::FilePath::CharType kArcBridgeSocketPath[] = |
dgreid | 500afee | 2016-01-05 18:16:54 | [diff] [blame] | 51 | FILE_PATH_LITERAL("/var/run/chrome/arc_bridge.sock"); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 52 | |
lhchavez | d896c6d | 2016-02-23 19:45:42 | [diff] [blame] | 53 | const char kArcBridgeSocketGroup[] = "arc-bridge"; |
| 54 | |
lhchavez | b078b99 | 2016-06-17 17:59:23 | [diff] [blame] | 55 | // 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. |
| 58 | void DoNothingInstanceStopped(bool) {} |
| 59 | |
| 60 | chromeos::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 | |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 70 | // 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. |
| 73 | bool 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. |
| 90 | bool 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. |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 112 | class ArcSessionImpl : public ArcSession, |
| 113 | public chromeos::SessionManagerClient::Observer { |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 114 | public: |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 115 | // The possible states of the session. In the normal flow, the state changes |
| 116 | // in the following sequence: |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 117 | // |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 118 | // NOT_STARTED |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 119 | // Start() -> |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 120 | // CREATING_SOCKET |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 121 | // CreateSocket() -> OnSocketCreated() -> |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 122 | // STARTING_INSTANCE |
| 123 | // -> OnInstanceStarted() -> |
| 124 | // CONNECTING_MOJO |
| 125 | // ConnectMojo() -> OnMojoConnected() -> |
| 126 | // RUNNING |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 127 | // |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 128 | // At any state, Stop() can be called. It does not immediately stop the |
| 129 | // instance, but will eventually stop it. |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 130 | // The actual stop will be notified via ArcSession::Observer::OnStopped(). |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 131 | // |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 132 | // When Stop() is called, it makes various behavior based on the current |
| 133 | // phase. |
lhchavez | aff66ee | 2016-01-05 19:43:13 | [diff] [blame] | 134 | // |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 135 | // NOT_STARTED: |
| 136 | // Do nothing. Immediately transition to the STOPPED state. |
hidehiko | 14f70fe | 2016-10-07 16:13:54 | [diff] [blame] | 137 | // CREATING_SOCKET: |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 138 | // The main task of the phase runs on BlockingPool thread. So, Stop() just |
hidehiko | 14f70fe | 2016-10-07 16:13:54 | [diff] [blame] | 139 | // sets the flag and return. On the main task completion, a callback |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 140 | // 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 |
hidehiko | 14f70fe | 2016-10-07 16:13:54 | [diff] [blame] | 145 | // 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. |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 150 | // CONNECTING_MOJO: |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 151 | // The main task runs on BlockingPool thread, but it is blocking call. |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 152 | // 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 |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 172 | // BlockingPool thread to cancel it to unblock the thread. In |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 173 | // 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. |
nya | 0c9dabf | 2016-07-13 19:54:27 | [diff] [blame] | 182 | // |
| 183 | // Note: Order of constants below matters. Please make sure to sort them |
| 184 | // in chronological order. |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 185 | enum class State { |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 186 | // ARC is not yet started. |
| 187 | NOT_STARTED, |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 188 | |
| 189 | // An UNIX socket is being created. |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 190 | CREATING_SOCKET, |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 191 | |
| 192 | // The request to start the instance has been sent. |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 193 | STARTING_INSTANCE, |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 194 | |
| 195 | // The instance has started. Waiting for it to connect to the IPC bridge. |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 196 | CONNECTING_MOJO, |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 197 | |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 198 | // The instance is fully set up. |
| 199 | RUNNING, |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 200 | |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 201 | // ARC is terminated. |
| 202 | STOPPED, |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 203 | }; |
| 204 | |
hidehiko | 01319c4e | 2016-12-08 05:59:46 | [diff] [blame] | 205 | ArcSessionImpl(ArcBridgeService* arc_bridge_service, |
| 206 | const scoped_refptr<base::TaskRunner>& blocking_task_runner); |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 207 | ~ArcSessionImpl() override; |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 208 | |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 209 | // ArcSession overrides: |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 210 | void Start() override; |
| 211 | void Stop() override; |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 212 | void OnShutdown() override; |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 213 | |
| 214 | private: |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 215 | // Creates the UNIX socket on a worker pool and then processes its file |
| 216 | // descriptor. |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 217 | static mojo::edk::ScopedPlatformHandle CreateSocket(); |
| 218 | void OnSocketCreated(mojo::edk::ScopedPlatformHandle fd); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 219 | |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 220 | // DBus callback for StartArcInstance(). |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 221 | void OnInstanceStarted(mojo::edk::ScopedPlatformHandle socket_fd, |
hidehiko | 02a11f5 | 2017-05-22 08:43:06 | [diff] [blame] | 222 | StartArcInstanceResult result, |
| 223 | const std::string& container_instance_id); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 224 | |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 225 | // Synchronously accepts a connection on |socket_fd| and then processes the |
| 226 | // connected socket's file descriptor. |
lhchavez | 50abf80a | 2017-01-25 01:04:53 | [diff] [blame] | 227 | static mojo::ScopedMessagePipeHandle ConnectMojo( |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 228 | mojo::edk::ScopedPlatformHandle socket_fd, |
| 229 | base::ScopedFD cancel_fd); |
lhchavez | 50abf80a | 2017-01-25 01:04:53 | [diff] [blame] | 230 | void OnMojoConnected(mojo::ScopedMessagePipeHandle server_pipe); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 231 | |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 232 | // Request to stop ARC instance via DBus. |
| 233 | void StopArcInstance(); |
lhchavez | b078b99 | 2016-06-17 17:59:23 | [diff] [blame] | 234 | |
| 235 | // chromeos::SessionManagerClient::Observer: |
hidehiko | 02a11f5 | 2017-05-22 08:43:06 | [diff] [blame] | 236 | void ArcInstanceStopped(bool clean, |
| 237 | const std::string& container_instance_id) override; |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 238 | |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 239 | // Completes the termination procedure. |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 240 | void OnStopped(ArcStopReason reason); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 241 | |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 242 | // Checks whether a function runs on the thread where the instance is |
| 243 | // created. |
Hidehiko Abe | a0cfefae | 2017-06-22 19:30:11 | [diff] [blame] | 244 | THREAD_CHECKER(thread_checker_); |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 245 | |
hidehiko | 01319c4e | 2016-12-08 05:59:46 | [diff] [blame] | 246 | // Owned by ArcServiceManager. |
| 247 | ArcBridgeService* const arc_bridge_service_; |
| 248 | |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 249 | // Task runner to run a blocking tasks. |
| 250 | scoped_refptr<base::TaskRunner> blocking_task_runner_; |
| 251 | |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 252 | // The state of the session. |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 253 | State state_ = State::NOT_STARTED; |
| 254 | |
| 255 | // When Stop() is called, this flag is set. |
| 256 | bool stop_requested_ = false; |
| 257 | |
hidehiko | 02a11f5 | 2017-05-22 08:43:06 | [diff] [blame] | 258 | // Container instance id passed from session_manager. |
| 259 | // Should be available only after OnInstanceStarted(). |
| 260 | std::string container_instance_id_; |
| 261 | |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 262 | // 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_; |
nya | 0c9dabf | 2016-07-13 19:54:27 | [diff] [blame] | 265 | |
hidehiko | a028d09 | 2016-10-05 02:20:23 | [diff] [blame] | 266 | // Mojo endpoint. |
| 267 | std::unique_ptr<mojom::ArcBridgeHost> arc_bridge_host_; |
| 268 | |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 269 | // WeakPtrFactory to use callbacks. |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 270 | base::WeakPtrFactory<ArcSessionImpl> weak_factory_; |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 271 | |
| 272 | private: |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 273 | DISALLOW_COPY_AND_ASSIGN(ArcSessionImpl); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 274 | }; |
| 275 | |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 276 | ArcSessionImpl::ArcSessionImpl( |
hidehiko | 01319c4e | 2016-12-08 05:59:46 | [diff] [blame] | 277 | ArcBridgeService* arc_bridge_service, |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 278 | const scoped_refptr<base::TaskRunner>& blocking_task_runner) |
hidehiko | 01319c4e | 2016-12-08 05:59:46 | [diff] [blame] | 279 | : arc_bridge_service_(arc_bridge_service), |
| 280 | blocking_task_runner_(blocking_task_runner), |
| 281 | weak_factory_(this) { |
lhchavez | b078b99 | 2016-06-17 17:59:23 | [diff] [blame] | 282 | chromeos::SessionManagerClient* client = GetSessionManagerClient(); |
| 283 | if (client == nullptr) |
| 284 | return; |
| 285 | client->AddObserver(this); |
| 286 | } |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 287 | |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 288 | ArcSessionImpl::~ArcSessionImpl() { |
Hidehiko Abe | a0cfefae | 2017-06-22 19:30:11 | [diff] [blame] | 289 | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 290 | DCHECK(state_ == State::NOT_STARTED || state_ == State::STOPPED); |
lhchavez | b078b99 | 2016-06-17 17:59:23 | [diff] [blame] | 291 | chromeos::SessionManagerClient* client = GetSessionManagerClient(); |
| 292 | if (client == nullptr) |
| 293 | return; |
| 294 | client->RemoveObserver(this); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 295 | } |
| 296 | |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 297 | void ArcSessionImpl::Start() { |
Hidehiko Abe | a0cfefae | 2017-06-22 19:30:11 | [diff] [blame] | 298 | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 299 | DCHECK_EQ(state_, State::NOT_STARTED); |
| 300 | VLOG(2) << "Starting ARC session."; |
hidehiko | 14f70fe | 2016-10-07 16:13:54 | [diff] [blame] | 301 | VLOG(2) << "Creating socket..."; |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 302 | |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 303 | state_ = State::CREATING_SOCKET; |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 304 | base::PostTaskAndReplyWithResult( |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 305 | blocking_task_runner_.get(), FROM_HERE, |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 306 | base::Bind(&ArcSessionImpl::CreateSocket), |
| 307 | base::Bind(&ArcSessionImpl::OnSocketCreated, weak_factory_.GetWeakPtr())); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 308 | } |
| 309 | |
| 310 | // static |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 311 | mojo::edk::ScopedPlatformHandle ArcSessionImpl::CreateSocket() { |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 312 | base::FilePath socket_path(kArcBridgeSocketPath); |
| 313 | |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 314 | 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; |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 318 | |
lhchavez | d896c6d | 2016-02-23 19:45:42 | [diff] [blame] | 319 | // 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"; |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 326 | return mojo::edk::ScopedPlatformHandle(); |
lhchavez | d896c6d | 2016-02-23 19:45:42 | [diff] [blame] | 327 | } |
| 328 | |
| 329 | if (!arc_bridge_group_res) { |
| 330 | LOG(ERROR) << "Group '" << kArcBridgeSocketGroup << "' not found"; |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 331 | return mojo::edk::ScopedPlatformHandle(); |
lhchavez | d896c6d | 2016-02-23 19:45:42 | [diff] [blame] | 332 | } |
| 333 | |
| 334 | if (HANDLE_EINTR(chown(kArcBridgeSocketPath, -1, arc_bridge_group.gr_gid)) < |
| 335 | 0) { |
| 336 | PLOG(ERROR) << "chown"; |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 337 | return mojo::edk::ScopedPlatformHandle(); |
lhchavez | d896c6d | 2016-02-23 19:45:42 | [diff] [blame] | 338 | } |
| 339 | |
| 340 | if (!base::SetPosixFilePermissions(socket_path, 0660)) { |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 341 | PLOG(ERROR) << "Could not set permissions: " << socket_path.value(); |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 342 | return mojo::edk::ScopedPlatformHandle(); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 343 | } |
| 344 | |
| 345 | return socket_fd; |
| 346 | } |
| 347 | |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 348 | void ArcSessionImpl::OnSocketCreated( |
| 349 | mojo::edk::ScopedPlatformHandle socket_fd) { |
Hidehiko Abe | a0cfefae | 2017-06-22 19:30:11 | [diff] [blame] | 350 | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 351 | DCHECK_EQ(state_, State::CREATING_SOCKET); |
| 352 | |
| 353 | if (stop_requested_) { |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 354 | VLOG(1) << "Stop() called while connecting"; |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 355 | OnStopped(ArcStopReason::SHUTDOWN); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 356 | return; |
| 357 | } |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 358 | |
| 359 | if (!socket_fd.is_valid()) { |
| 360 | LOG(ERROR) << "ARC: Error creating socket"; |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 361 | OnStopped(ArcStopReason::GENERIC_BOOT_FAILURE); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 362 | return; |
| 363 | } |
nya | 73ba213 | 2016-06-07 05:21:28 | [diff] [blame] | 364 | |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 365 | VLOG(2) << "Socket is created. Starting ARC instance..."; |
| 366 | state_ = State::STARTING_INSTANCE; |
nya | 73ba213 | 2016-06-07 05:21:28 | [diff] [blame] | 367 | 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 | |
xiaohuic | 2f57e3f7 | 2017-05-31 01:29:53 | [diff] [blame] | 372 | const bool skip_boot_completed_broadcast = |
xzhou | 938368c | 2016-10-04 04:33:17 | [diff] [blame] | 373 | !base::FeatureList::IsEnabled(arc::kBootCompletedBroadcastFeature); |
| 374 | |
xiaohuic | 6bc1dc0 | 2017-05-08 17:52:01 | [diff] [blame] | 375 | // We only enable /vendor/priv-app when voice interaction is enabled because |
| 376 | // voice interaction service apk would be bundled in this location. |
xiaohuic | 2f57e3f7 | 2017-05-31 01:29:53 | [diff] [blame] | 377 | const bool scan_vendor_priv_app = |
xiaohuic | 6bc1dc0 | 2017-05-08 17:52:01 | [diff] [blame] | 378 | chromeos::switches::IsVoiceInteractionEnabled(); |
| 379 | |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 380 | chromeos::SessionManagerClient* session_manager_client = |
| 381 | chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); |
| 382 | session_manager_client->StartArcInstance( |
yusukes | 3ad40007 | 2017-06-09 16:54:34 | [diff] [blame] | 383 | chromeos::SessionManagerClient::ArcStartupMode::FULL, cryptohome_id, |
| 384 | skip_boot_completed_broadcast, scan_vendor_priv_app, |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 385 | base::Bind(&ArcSessionImpl::OnInstanceStarted, weak_factory_.GetWeakPtr(), |
| 386 | base::Passed(&socket_fd))); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 387 | } |
| 388 | |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 389 | void ArcSessionImpl::OnInstanceStarted( |
| 390 | mojo::edk::ScopedPlatformHandle socket_fd, |
hidehiko | 02a11f5 | 2017-05-22 08:43:06 | [diff] [blame] | 391 | StartArcInstanceResult result, |
| 392 | const std::string& container_instance_id) { |
Hidehiko Abe | a0cfefae | 2017-06-22 19:30:11 | [diff] [blame] | 393 | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 394 | DCHECK_EQ(state_, State::STARTING_INSTANCE); |
hidehiko | 02a11f5 | 2017-05-22 08:43:06 | [diff] [blame] | 395 | container_instance_id_ = container_instance_id; |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 396 | |
| 397 | if (stop_requested_) { |
hidehiko | 14f70fe | 2016-10-07 16:13:54 | [diff] [blame] | 398 | if (result == StartArcInstanceResult::SUCCESS) { |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 399 | // The ARC instance has started to run. Request to stop. |
| 400 | StopArcInstance(); |
| 401 | return; |
| 402 | } |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 403 | OnStopped(ArcStopReason::SHUTDOWN); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 404 | return; |
| 405 | } |
| 406 | |
hidehiko | 14f70fe | 2016-10-07 16:13:54 | [diff] [blame] | 407 | if (result != StartArcInstanceResult::SUCCESS) { |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 408 | LOG(ERROR) << "Failed to start ARC instance"; |
hidehiko | 14f70fe | 2016-10-07 16:13:54 | [diff] [blame] | 409 | OnStopped(result == StartArcInstanceResult::LOW_FREE_DISK_SPACE |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 410 | ? ArcStopReason::LOW_DISK_SPACE |
| 411 | : ArcStopReason::GENERIC_BOOT_FAILURE); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 412 | return; |
| 413 | } |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 414 | |
| 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_)) { |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 422 | OnStopped(ArcStopReason::GENERIC_BOOT_FAILURE); |
lhchavez | b078b99 | 2016-06-17 17:59:23 | [diff] [blame] | 423 | return; |
| 424 | } |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 425 | |
| 426 | base::PostTaskAndReplyWithResult( |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 427 | blocking_task_runner_.get(), FROM_HERE, |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 428 | base::Bind(&ArcSessionImpl::ConnectMojo, base::Passed(&socket_fd), |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 429 | base::Passed(&cancel_fd)), |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 430 | base::Bind(&ArcSessionImpl::OnMojoConnected, weak_factory_.GetWeakPtr())); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 431 | } |
| 432 | |
| 433 | // static |
lhchavez | 50abf80a | 2017-01-25 01:04:53 | [diff] [blame] | 434 | mojo::ScopedMessagePipeHandle ArcSessionImpl::ConnectMojo( |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 435 | mojo::edk::ScopedPlatformHandle socket_fd, |
| 436 | base::ScopedFD cancel_fd) { |
| 437 | if (!WaitForSocketReadable(socket_fd.get().handle, cancel_fd.get())) { |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 438 | VLOG(1) << "Mojo connection was cancelled."; |
lhchavez | 50abf80a | 2017-01-25 01:04:53 | [diff] [blame] | 439 | return mojo::ScopedMessagePipeHandle(); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 440 | } |
| 441 | |
sammc | cde36206 | 2016-11-23 02:33:57 | [diff] [blame] | 442 | 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()) { |
lhchavez | 50abf80a | 2017-01-25 01:04:53 | [diff] [blame] | 446 | return mojo::ScopedMessagePipeHandle(); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 447 | } |
lhchavez | 47e4727 | 2016-02-01 21:09:20 | [diff] [blame] | 448 | |
kcwu | e5654af | 2016-04-16 02:40:52 | [diff] [blame] | 449 | // Hardcode pid 0 since it is unused in mojo. |
| 450 | const base::ProcessHandle kUnusedChildProcessHandle = 0; |
amistry | 15cf85cc | 2016-05-26 23:45:35 | [diff] [blame] | 451 | mojo::edk::PlatformChannelPair channel_pair; |
Ken Rockot | ea1716a0 | 2017-05-11 05:49:10 | [diff] [blame] | 452 | mojo::edk::OutgoingBrokerClientInvitation invitation; |
| 453 | |
| 454 | std::string token = mojo::edk::GenerateRandomToken(); |
| 455 | mojo::ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(token); |
| 456 | |
Ken Rockot | 29bd77b | 2017-05-11 22:24:49 | [diff] [blame] | 457 | invitation.Send( |
| 458 | kUnusedChildProcessHandle, |
| 459 | mojo::edk::ConnectionParams(mojo::edk::TransportProtocol::kLegacy, |
| 460 | channel_pair.PassServerHandle())); |
lhchavez | 47e4727 | 2016-02-01 21:09:20 | [diff] [blame] | 461 | |
| 462 | mojo::edk::ScopedPlatformHandleVectorPtr handles( |
amistry | 15cf85cc | 2016-05-26 23:45:35 | [diff] [blame] | 463 | new mojo::edk::PlatformHandleVector{ |
| 464 | channel_pair.PassClientHandle().release()}); |
lhchavez | 47e4727 | 2016-02-01 21:09:20 | [diff] [blame] | 465 | |
lhchavez | 50abf80a | 2017-01-25 01:04:53 | [diff] [blame] | 466 | // 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()}}; |
lhchavez | 47e4727 | 2016-02-01 21:09:20 | [diff] [blame] | 472 | ssize_t result = mojo::edk::PlatformChannelSendmsgWithHandles( |
lhchavez | 50abf80a | 2017-01-25 01:04:53 | [diff] [blame] | 473 | scoped_fd.get(), iov, sizeof(iov) / sizeof(iov[0]), handles->data(), |
| 474 | handles->size()); |
lhchavez | 47e4727 | 2016-02-01 21:09:20 | [diff] [blame] | 475 | if (result == -1) { |
| 476 | PLOG(ERROR) << "sendmsg"; |
lhchavez | 50abf80a | 2017-01-25 01:04:53 | [diff] [blame] | 477 | return mojo::ScopedMessagePipeHandle(); |
lhchavez | 47e4727 | 2016-02-01 21:09:20 | [diff] [blame] | 478 | } |
| 479 | |
rockot | 1039b82 | 2017-02-09 23:19:41 | [diff] [blame] | 480 | return pipe; |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 481 | } |
| 482 | |
lhchavez | 50abf80a | 2017-01-25 01:04:53 | [diff] [blame] | 483 | void ArcSessionImpl::OnMojoConnected( |
| 484 | mojo::ScopedMessagePipeHandle server_pipe) { |
Hidehiko Abe | a0cfefae | 2017-06-22 19:30:11 | [diff] [blame] | 485 | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 486 | DCHECK_EQ(state_, State::CONNECTING_MOJO); |
| 487 | accept_cancel_pipe_.reset(); |
| 488 | |
| 489 | if (stop_requested_) { |
| 490 | StopArcInstance(); |
| 491 | return; |
| 492 | } |
| 493 | |
lhchavez | 47e4727 | 2016-02-01 21:09:20 | [diff] [blame] | 494 | if (!server_pipe.is_valid()) { |
| 495 | LOG(ERROR) << "Invalid pipe"; |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 496 | StopArcInstance(); |
lhchavez | 47e4727 | 2016-02-01 21:09:20 | [diff] [blame] | 497 | return; |
| 498 | } |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 499 | |
leon.han | 89032291 | 2016-04-16 23:22:53 | [diff] [blame] | 500 | mojom::ArcBridgeInstancePtr instance; |
| 501 | instance.Bind(mojo::InterfacePtrInfo<mojom::ArcBridgeInstance>( |
| 502 | std::move(server_pipe), 0u)); |
hidehiko | 01319c4e | 2016-12-08 05:59:46 | [diff] [blame] | 503 | arc_bridge_host_ = base::MakeUnique<ArcBridgeHostImpl>(arc_bridge_service_, |
| 504 | std::move(instance)); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 505 | |
| 506 | VLOG(2) << "Mojo is connected. ARC is running."; |
| 507 | state_ = State::RUNNING; |
yusukes | 5e2934b | 2016-10-12 19:24:14 | [diff] [blame] | 508 | for (auto& observer : observer_list_) |
hidehiko | 9bc9ba5 | 2016-12-14 09:01:18 | [diff] [blame] | 509 | observer.OnSessionReady(); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 510 | } |
| 511 | |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 512 | void ArcSessionImpl::Stop() { |
Hidehiko Abe | a0cfefae | 2017-06-22 19:30:11 | [diff] [blame] | 513 | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 514 | 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_) |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 519 | return; |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 520 | |
| 521 | stop_requested_ = true; |
hidehiko | a028d09 | 2016-10-05 02:20:23 | [diff] [blame] | 522 | arc_bridge_host_.reset(); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 523 | switch (state_) { |
| 524 | case State::NOT_STARTED: |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 525 | OnStopped(ArcStopReason::SHUTDOWN); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 526 | return; |
| 527 | |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 528 | 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; |
hidehiko | 14f70fe | 2016-10-07 16:13:54 | [diff] [blame] | 535 | // - On socket creating, it is ok to simply ignore such cases, |
| 536 | // because we no-longer continue the bootstrap procedure. |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 537 | // - 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: |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 543 | // Mojo connection is being waited on a BlockingPool thread. |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 544 | // Request to cancel it. Following stopping procedure will run |
| 545 | // in its callback. |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 546 | 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; |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 557 | } |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 558 | } |
| 559 | |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 560 | void ArcSessionImpl::StopArcInstance() { |
Hidehiko Abe | a0cfefae | 2017-06-22 19:30:11 | [diff] [blame] | 561 | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 562 | DCHECK(state_ == State::STARTING_INSTANCE || |
| 563 | state_ == State::CONNECTING_MOJO || state_ == State::RUNNING); |
| 564 | |
lhchavez | b078b99 | 2016-06-17 17:59:23 | [diff] [blame] | 565 | // Notification will arrive through ArcInstanceStopped(). |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 566 | VLOG(2) << "Requesting to stop ARC instance"; |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 567 | chromeos::SessionManagerClient* session_manager_client = |
| 568 | chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); |
lhchavez | b078b99 | 2016-06-17 17:59:23 | [diff] [blame] | 569 | session_manager_client->StopArcInstance( |
| 570 | base::Bind(&DoNothingInstanceStopped)); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 571 | } |
| 572 | |
hidehiko | 02a11f5 | 2017-05-22 08:43:06 | [diff] [blame] | 573 | void ArcSessionImpl::ArcInstanceStopped( |
| 574 | bool clean, |
| 575 | const std::string& container_instance_id) { |
Hidehiko Abe | a0cfefae | 2017-06-22 19:30:11 | [diff] [blame] | 576 | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 577 | VLOG(1) << "Notified that ARC instance is stopped " |
| 578 | << (clean ? "cleanly" : "uncleanly"); |
| 579 | |
hidehiko | 02a11f5 | 2017-05-22 08:43:06 | [diff] [blame] | 580 | 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 | |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 589 | // In case that crash happens during before the Mojo channel is connected, |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 590 | // unlock the BlockingPool thread. |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 591 | accept_cancel_pipe_.reset(); |
| 592 | |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 593 | ArcStopReason reason; |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 594 | if (stop_requested_) { |
| 595 | // If the ARC instance is stopped after its explicit request, |
| 596 | // return SHUTDOWN. |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 597 | reason = ArcStopReason::SHUTDOWN; |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 598 | } 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. |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 602 | reason = ArcStopReason::GENERIC_BOOT_FAILURE; |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 603 | } else { |
| 604 | // Otherwise, this is caused by CRASH occured inside of the ARC instance. |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 605 | reason = ArcStopReason::CRASH; |
nya | 0c9dabf | 2016-07-13 19:54:27 | [diff] [blame] | 606 | } |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 607 | OnStopped(reason); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 608 | } |
| 609 | |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 610 | void ArcSessionImpl::OnStopped(ArcStopReason reason) { |
Hidehiko Abe | a0cfefae | 2017-06-22 19:30:11 | [diff] [blame] | 611 | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 612 | // OnStopped() should be called once per instance. |
| 613 | DCHECK_NE(state_, State::STOPPED); |
| 614 | VLOG(2) << "ARC session is stopped."; |
hidehiko | a028d09 | 2016-10-05 02:20:23 | [diff] [blame] | 615 | arc_bridge_host_.reset(); |
hidehiko | 321d423c | 2016-09-30 15:51:30 | [diff] [blame] | 616 | state_ = State::STOPPED; |
yusukes | 5e2934b | 2016-10-12 19:24:14 | [diff] [blame] | 617 | for (auto& observer : observer_list_) |
hidehiko | 9bc9ba5 | 2016-12-14 09:01:18 | [diff] [blame] | 618 | observer.OnSessionStopped(reason); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 619 | } |
| 620 | |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 621 | void ArcSessionImpl::OnShutdown() { |
Hidehiko Abe | a0cfefae | 2017-06-22 19:30:11 | [diff] [blame] | 622 | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 623 | 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. |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 643 | OnStopped(ArcStopReason::SHUTDOWN); |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 644 | } |
| 645 | |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 646 | } // namespace |
| 647 | |
hidehiko | 88160c8 | 2016-10-18 09:43:47 | [diff] [blame] | 648 | ArcSession::ArcSession() = default; |
| 649 | ArcSession::~ArcSession() = default; |
hidehiko | a028d09 | 2016-10-05 02:20:23 | [diff] [blame] | 650 | |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 651 | void ArcSession::AddObserver(Observer* observer) { |
hidehiko | a028d09 | 2016-10-05 02:20:23 | [diff] [blame] | 652 | observer_list_.AddObserver(observer); |
| 653 | } |
| 654 | |
hidehiko | ae82e3a | 2017-03-01 20:05:39 | [diff] [blame] | 655 | void ArcSession::RemoveObserver(Observer* observer) { |
hidehiko | a028d09 | 2016-10-05 02:20:23 | [diff] [blame] | 656 | observer_list_.RemoveObserver(observer); |
| 657 | } |
| 658 | |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 659 | // static |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 660 | std::unique_ptr<ArcSession> ArcSession::Create( |
hidehiko | 01319c4e | 2016-12-08 05:59:46 | [diff] [blame] | 661 | ArcBridgeService* arc_bridge_service, |
hidehiko | 1362287 | 2016-10-21 10:05:09 | [diff] [blame] | 662 | const scoped_refptr<base::TaskRunner>& blocking_task_runner) { |
hidehiko | 01319c4e | 2016-12-08 05:59:46 | [diff] [blame] | 663 | return base::MakeUnique<ArcSessionImpl>(arc_bridge_service, |
| 664 | blocking_task_runner); |
lhchavez | f1c05537 | 2015-12-07 22:51:18 | [diff] [blame] | 665 | } |
| 666 | |
| 667 | } // namespace arc |