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

Side by Side Diff: media/capture/video/chromeos/arc_camera3_service.cc

Issue 2878233002: media: add ArcCamera3Service Mojo service (Closed)
Patch Set: Created 3 years, 7 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
OLDNEW
(Empty)
1 // Copyright 2017 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 "media/capture/video/chromeos/arc_camera3_service.h"
6
7 #include <fcntl.h>
8 #include <grp.h>
9 #include <poll.h>
10 #include <sys/uio.h>
11
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "mojo/edk/embedder/embedder.h"
16 #include "mojo/edk/embedder/named_platform_handle.h"
17 #include "mojo/edk/embedder/named_platform_handle_utils.h"
18 #include "mojo/edk/embedder/pending_process_connection.h"
19 #include "mojo/edk/embedder/platform_channel_pair.h"
20 #include "mojo/edk/embedder/platform_channel_utils_posix.h"
21 #include "mojo/edk/embedder/platform_handle_vector.h"
22 #include "mojo/edk/embedder/scoped_platform_handle.h"
23
24 namespace media {
25
26 namespace {
27
28 const base::FilePath::CharType kArcCamera3SocketPath[] =
29 "/var/run/camera/camera3.sock";
30 const char kArcCameraGroup[] = "arc-camera";
31
32 // Creates a pipe. Returns true on success, otherwise false.
33 // On success, |read_fd| will be set to the fd of the read side, and
34 // |write_fd| will be set to the one of write side.
35 static bool CreatePipe(base::ScopedFD* read_fd, base::ScopedFD* write_fd) {
36 int fds[2];
37 if (pipe2(fds, O_NONBLOCK | O_CLOEXEC) < 0) {
38 PLOG(ERROR) << "pipe2 failed: ";
39 return false;
40 }
41
42 read_fd->reset(fds[0]);
43 write_fd->reset(fds[1]);
44 return true;
45 }
46
47 // Waits until |raw_socket_fd| is readable.
48 // The operation may be cancelled originally triggered by user interaction to
49 // disable ARC, or ARC instance is unexpectedly stopped (e.g. crash).
50 // To notify such a situation, |raw_cancel_fd| is also passed to here, and the
51 // write side will be closed in such a case.
52 static bool WaitForSocketReadable(int raw_socket_fd, int raw_cancel_fd) {
53 struct pollfd fds[2] = {
54 {raw_socket_fd, POLLIN, 0}, {raw_cancel_fd, POLLIN, 0},
55 };
56
57 if (HANDLE_EINTR(poll(fds, arraysize(fds), -1)) <= 0) {
58 PLOG(ERROR) << "poll failed: ";
59 return false;
60 }
61
62 if (fds[1].revents) {
63 VLOG(1) << "Stop() was called";
64 return false;
65 }
66
67 DCHECK(fds[0].revents);
68 return true;
69 }
70
71 class MojoCameraClient : public ArcCamera3Service::CameraClientObserver {
72 public:
73 explicit MojoCameraClient(arc::mojom::ArcCamera3ClientPtr client)
74 : client_(std::move(client)) {}
75
76 void NotifyCameraHalRegistered(
77 arc::mojom::CameraModulePtr camera_module) override {
78 client_->NotifyCameraHalRegistered(std::move(camera_module));
79 }
80
81 arc::mojom::ArcCamera3ClientPtr& client() { return client_; }
82
83 private:
84 arc::mojom::ArcCamera3ClientPtr client_;
85 DISALLOW_IMPLICIT_CONSTRUCTORS(MojoCameraClient);
86 };
87
88 } // namespace
89
90 ArcCamera3Service::CameraHal::CameraHal(arc::mojom::ArcCamera3HalPtr camera_hal)
91 : camera_hal_(std::move(camera_hal)) {}
92
93 void ArcCamera3Service::CameraHal::OpenCameraHal(
94 const base::Callback<void(arc::mojom::CameraModulePtr camera_module)>
95 callback) {
96 camera_hal_->OpenCameraHal(callback);
97 }
98
99 ArcCamera3Service::ArcCamera3Service()
100 : proxy_thread_("ProxyThread"), blocking_io_thread_("BlockingIOThread") {}
101
102 ArcCamera3Service::~ArcCamera3Service() {
103 VLOG(1) << "Stopping ArcCamera3Service...";
104 proxy_thread_.task_runner()->PostTask(
105 FROM_HERE, base::Bind(&ArcCamera3Service::StopOnProxyThread,
106 base::Unretained(this)));
107 proxy_thread_.Stop();
108 blocking_io_thread_.Stop();
109 VLOG(1) << "ArcCamera3Service stopped";
110 }
111
112 // static
113 ArcCamera3Service* ArcCamera3Service::GetInstance() {
114 return base::Singleton<ArcCamera3Service>::get();
115 }
116
117 bool ArcCamera3Service::Start() {
118 DCHECK(!proxy_thread_.IsRunning());
119 DCHECK(!blocking_io_thread_.IsRunning());
120
121 proxy_thread_.Start();
122 blocking_io_thread_.Start();
123 proxy_task_runner_ = proxy_thread_.task_runner();
124 blocking_io_task_runner_ = blocking_io_thread_.task_runner();
125
126 blocking_io_task_runner_->PostTask(
127 FROM_HERE,
128 base::Bind(&ArcCamera3Service::CreateSocket, base::Unretained(this)));
129 return true;
130 }
131
132 void ArcCamera3Service::StopOnProxyThread() {
133 DCHECK(proxy_task_runner_->BelongsToCurrentThread());
134 base::DeleteFile(base::FilePath(kArcCamera3SocketPath),
135 /* recursive */ false);
136 // Close |cancel_pipe_| to quit the loop in WaitForIncomingConnection.
137 cancel_pipe_.reset();
138 client_observers_.clear();
139 camera_hal_.reset();
140 bindings_.clear();
141 }
142
143 void ArcCamera3Service::AddClientObserver(
144 std::unique_ptr<CameraClientObserver> observer) {
145 if (camera_hal_) {
146 proxy_task_runner_->PostTask(
147 FROM_HERE,
148 base::Bind(&CameraHal::OpenCameraHal, camera_hal_->AsWeakPtr(),
149 base::Bind(&CameraClientObserver::NotifyCameraHalRegistered,
150 base::AsWeakPtr(observer.get()))));
151 }
152 client_observers_.insert(std::move(observer));
153 }
154
155 void ArcCamera3Service::RegisterCameraHal(
156 arc::mojom::ArcCamera3HalPtr camera_hal) {
157 DCHECK(proxy_task_runner_->BelongsToCurrentThread());
158 DCHECK(!camera_hal_);
159 camera_hal.set_connection_error_handler(base::Bind(
160 &ArcCamera3Service::OnCameraHalConnectionError, base::Unretained(this)));
161 camera_hal_.reset(new CameraHal(std::move(camera_hal)));
162 VLOG(1) << "Camera HAL registered";
163 for (auto& observer : client_observers_) {
164 proxy_task_runner_->PostTask(
165 FROM_HERE,
166 base::Bind(&CameraHal::OpenCameraHal, camera_hal_->AsWeakPtr(),
167 base::Bind(&CameraClientObserver::NotifyCameraHalRegistered,
168 base::AsWeakPtr(observer.get()))));
169 }
170 }
171
172 void ArcCamera3Service::RegisterClient(arc::mojom::ArcCamera3ClientPtr client) {
173 DCHECK(proxy_task_runner_->BelongsToCurrentThread());
174 MojoCameraClient* camera_client = new MojoCameraClient(std::move(client));
175 std::unique_ptr<CameraClientObserver> client_observer(camera_client);
176 camera_client->client().set_connection_error_handler(base::Bind(
177 &ArcCamera3Service::OnMojoClientConnectionError, base::Unretained(this),
178 base::Unretained(client_observer.get())));
179 AddClientObserver(std::move(client_observer));
180 VLOG(1) << "New mojo client registered";
181 }
182
183 void ArcCamera3Service::CreateSocket() {
184 DCHECK(blocking_io_task_runner_->BelongsToCurrentThread());
185
186 base::FilePath socket_path(kArcCamera3SocketPath);
187 mojo::edk::ScopedPlatformHandle socket_fd = mojo::edk::CreateServerHandle(
188 mojo::edk::NamedPlatformHandle(socket_path.value()));
189 if (!socket_fd.is_valid()) {
190 LOG(ERROR) << "Failed to create the socket file: " << kArcCamera3SocketPath;
191 return;
192 }
193
194 // Change permissions on the socket.
195 struct group arc_camera_group;
196 struct group* result = nullptr;
197 char buf[1024];
198 if (HANDLE_EINTR(getgrnam_r(kArcCameraGroup, &arc_camera_group, buf,
199 sizeof(buf), &result)) < 0) {
200 PLOG(ERROR) << "getgrnam_r failed: ";
201 return;
202 }
203
204 if (!result) {
205 LOG(ERROR) << "Group '" << kArcCameraGroup << "' not found";
206 return;
207 }
208
209 if (HANDLE_EINTR(chown(kArcCamera3SocketPath, -1, arc_camera_group.gr_gid)) <
210 0) {
211 PLOG(ERROR) << "chown failed: ";
212 return;
213 }
214
215 if (!base::SetPosixFilePermissions(socket_path, 0660)) {
216 PLOG(ERROR) << "Could not set permissions: " << socket_path.value() << ": ";
217 return;
218 }
219
220 blocking_io_task_runner_->PostTask(
221 FROM_HERE, base::Bind(&ArcCamera3Service::CreateServiceLoop,
222 base::Unretained(this), base::Passed(&socket_fd)));
223 }
224
225 void ArcCamera3Service::CreateServiceLoop(
226 mojo::edk::ScopedPlatformHandle socket_fd) {
227 DCHECK(blocking_io_task_runner_->BelongsToCurrentThread());
228 DCHECK(!proxy_fd_.is_valid());
229 DCHECK(!cancel_pipe_.is_valid());
230 DCHECK(socket_fd.is_valid());
231
232 proxy_fd_ = std::move(socket_fd);
233
234 base::ScopedFD cancel_fd;
235 if (!CreatePipe(&cancel_fd, &cancel_pipe_)) {
236 LOG(ERROR) << "Failed to create cancel pipe";
237 return;
238 }
239
240 blocking_io_task_runner_->PostTask(
241 FROM_HERE, base::Bind(&ArcCamera3Service::WaitForIncomingConnection,
242 base::Unretained(this), base::Passed(&cancel_fd)));
243 VLOG(1) << "ArcCamera3Service started";
244 }
245
246 void ArcCamera3Service::WaitForIncomingConnection(base::ScopedFD cancel_fd) {
247 DCHECK(blocking_io_task_runner_->BelongsToCurrentThread());
248 DCHECK(proxy_fd_.is_valid());
249 VLOG(1) << "Waiting for incoming connection...";
250
251 if (!proxy_fd_.is_valid()) {
252 LOG(ERROR) << "Invalid proxy fd";
253 return;
254 }
255
256 if (!WaitForSocketReadable(proxy_fd_.get().handle, cancel_fd.get())) {
257 VLOG(1) << "Quit ArcCamera3Service IO thread";
258 return;
259 }
260
261 mojo::edk::ScopedPlatformHandle accepted_fd;
262 if (mojo::edk::ServerAcceptConnection(proxy_fd_.get(), &accepted_fd, false) &&
263 accepted_fd.is_valid()) {
264 VLOG(1) << "Accepted a connection";
265 // Hardcode pid 0 since it is unused in mojo.
266 const base::ProcessHandle kUnusedChildProcessHandle = 0;
267 mojo::edk::PlatformChannelPair channel_pair;
268 mojo::edk::PendingProcessConnection process;
269 process.Connect(
270 kUnusedChildProcessHandle,
271 mojo::edk::ConnectionParams(channel_pair.PassServerHandle()));
272
273 mojo::edk::ScopedPlatformHandleVectorPtr handles(
274 new mojo::edk::PlatformHandleVector{
275 channel_pair.PassClientHandle().release()});
276
277 std::string token;
278 mojo::ScopedMessagePipeHandle message_pipe =
279 process.CreateMessagePipe(&token);
280
281 struct iovec iov = {const_cast<char*>(token.c_str()), token.length()};
282 ssize_t result = mojo::edk::PlatformChannelSendmsgWithHandles(
283 accepted_fd.get(), &iov, 1, handles->data(), handles->size());
284 if (result == -1) {
285 PLOG(ERROR) << "sendmsg failed: ";
286 } else {
287 proxy_task_runner_->PostTask(
288 FROM_HERE,
289 base::Bind(&ArcCamera3Service::OnPeerConnected,
290 base::Unretained(this), base::Passed(&message_pipe)));
291 }
292 }
293
294 // Continue waiting for new connections.
295 blocking_io_task_runner_->PostTask(
296 FROM_HERE, base::Bind(&ArcCamera3Service::WaitForIncomingConnection,
297 base::Unretained(this), base::Passed(&cancel_fd)));
298 }
299
300 void ArcCamera3Service::OnPeerConnected(
301 mojo::ScopedMessagePipeHandle message_pipe) {
302 DCHECK(proxy_task_runner_->BelongsToCurrentThread());
303 auto binding = base::MakeUnique<mojo::Binding<arc::mojom::ArcCamera3Service>>(
304 this, std::move(message_pipe));
305 bindings_.push_back(std::move(binding));
306 VLOG(1) << "New binding added";
307 }
308
309 void ArcCamera3Service::OnCameraHalConnectionError() {
310 DCHECK(proxy_task_runner_->BelongsToCurrentThread());
311 VLOG(1) << "Camera HAL connection lost";
312 camera_hal_.reset();
313 }
314
315 void ArcCamera3Service::OnMojoClientConnectionError(
316 CameraClientObserver* client_observer) {
317 DCHECK(proxy_task_runner_->BelongsToCurrentThread());
318 for (auto& it : client_observers_) {
319 if (it.get() == client_observer) {
320 client_observers_.erase(it);
321 VLOG(1) << "Mojo client connection lost";
322 break;
323 }
324 }
325 }
326
327 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698