OLD | NEW |
| (Empty) |
1 // Copyright 2013 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 "mojo/edk/system/channel.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/logging.h" | |
11 #include "base/macros.h" | |
12 #include "base/strings/stringprintf.h" | |
13 #include "mojo/edk/embedder/platform_handle_vector.h" | |
14 #include "mojo/edk/system/endpoint_relayer.h" | |
15 #include "mojo/edk/system/transport_data.h" | |
16 | |
17 namespace mojo { | |
18 namespace system { | |
19 | |
20 namespace { | |
21 | |
22 struct SerializedEndpoint { | |
23 // This is the endpoint ID on the receiving side, and should be a "remote ID". | |
24 // (The receiving side should already have had an endpoint attached and been | |
25 // run via the |Channel|s. This endpoint will have both IDs assigned, so this | |
26 // ID is only needed to associate that endpoint with a particular dispatcher.) | |
27 ChannelEndpointId receiver_endpoint_id; | |
28 }; | |
29 | |
30 } // namespace | |
31 | |
32 Channel::Channel(embedder::PlatformSupport* platform_support) | |
33 : platform_support_(platform_support), | |
34 is_running_(false), | |
35 is_shutting_down_(false), | |
36 channel_manager_(nullptr) { | |
37 } | |
38 | |
39 void Channel::Init(scoped_ptr<RawChannel> raw_channel) { | |
40 DCHECK(creation_thread_checker_.CalledOnValidThread()); | |
41 DCHECK(raw_channel); | |
42 | |
43 // No need to take |lock_|, since this must be called before this object | |
44 // becomes thread-safe. | |
45 DCHECK(!is_running_); | |
46 raw_channel_ = raw_channel.Pass(); | |
47 raw_channel_->Init(this); | |
48 is_running_ = true; | |
49 } | |
50 | |
51 void Channel::SetChannelManager(ChannelManager* channel_manager) { | |
52 DCHECK(channel_manager); | |
53 | |
54 base::AutoLock locker(lock_); | |
55 DCHECK(!is_shutting_down_); | |
56 DCHECK(!channel_manager_); | |
57 channel_manager_ = channel_manager; | |
58 } | |
59 | |
60 void Channel::Shutdown() { | |
61 DCHECK(creation_thread_checker_.CalledOnValidThread()); | |
62 | |
63 IdToEndpointMap to_destroy; | |
64 { | |
65 base::AutoLock locker(lock_); | |
66 if (!is_running_) | |
67 return; | |
68 | |
69 // Note: Don't reset |raw_channel_|, in case we're being called from within | |
70 // |OnReadMessage()| or |OnError()|. | |
71 raw_channel_->Shutdown(); | |
72 is_running_ = false; | |
73 | |
74 // We need to deal with it outside the lock. | |
75 std::swap(to_destroy, local_id_to_endpoint_map_); | |
76 } | |
77 | |
78 size_t num_live = 0; | |
79 size_t num_zombies = 0; | |
80 for (IdToEndpointMap::iterator it = to_destroy.begin(); | |
81 it != to_destroy.end(); ++it) { | |
82 if (it->second) { | |
83 num_live++; | |
84 it->second->DetachFromChannel(); | |
85 } else { | |
86 num_zombies++; | |
87 } | |
88 } | |
89 DVLOG_IF(2, num_live || num_zombies) << "Shut down Channel with " << num_live | |
90 << " live endpoints and " << num_zombies | |
91 << " zombies"; | |
92 } | |
93 | |
94 void Channel::WillShutdownSoon() { | |
95 base::AutoLock locker(lock_); | |
96 is_shutting_down_ = true; | |
97 channel_manager_ = nullptr; | |
98 } | |
99 | |
100 void Channel::SetBootstrapEndpoint(scoped_refptr<ChannelEndpoint> endpoint) { | |
101 DCHECK(endpoint); | |
102 | |
103 // Used for both local and remote IDs. | |
104 ChannelEndpointId bootstrap_id = ChannelEndpointId::GetBootstrap(); | |
105 | |
106 { | |
107 base::AutoLock locker(lock_); | |
108 | |
109 DLOG_IF(WARNING, is_shutting_down_) | |
110 << "SetBootstrapEndpoint() while shutting down"; | |
111 | |
112 // Bootstrap endpoint should be the first. | |
113 DCHECK(local_id_to_endpoint_map_.empty()); | |
114 | |
115 local_id_to_endpoint_map_[bootstrap_id] = endpoint; | |
116 } | |
117 | |
118 endpoint->AttachAndRun(this, bootstrap_id, bootstrap_id); | |
119 } | |
120 | |
121 bool Channel::WriteMessage(scoped_ptr<MessageInTransit> message) { | |
122 base::AutoLock locker(lock_); | |
123 if (!is_running_) { | |
124 // TODO(vtl): I think this is probably not an error condition, but I should | |
125 // think about it (and the shutdown sequence) more carefully. | |
126 LOG(WARNING) << "WriteMessage() after shutdown"; | |
127 return false; | |
128 } | |
129 | |
130 DLOG_IF(WARNING, is_shutting_down_) << "WriteMessage() while shutting down"; | |
131 return raw_channel_->WriteMessage(message.Pass()); | |
132 } | |
133 | |
134 bool Channel::IsWriteBufferEmpty() { | |
135 base::AutoLock locker(lock_); | |
136 if (!is_running_) | |
137 return true; | |
138 return raw_channel_->IsWriteBufferEmpty(); | |
139 } | |
140 | |
141 void Channel::DetachEndpoint(ChannelEndpoint* endpoint, | |
142 ChannelEndpointId local_id, | |
143 ChannelEndpointId remote_id) { | |
144 DCHECK(endpoint); | |
145 DCHECK(local_id.is_valid()); | |
146 | |
147 if (!remote_id.is_valid()) | |
148 return; // Nothing to do. | |
149 | |
150 { | |
151 base::AutoLock locker_(lock_); | |
152 if (!is_running_) | |
153 return; | |
154 | |
155 IdToEndpointMap::iterator it = local_id_to_endpoint_map_.find(local_id); | |
156 // We detach immediately if we receive a remove message, so it's possible | |
157 // that the local ID is no longer in |local_id_to_endpoint_map_|, or even | |
158 // that it's since been reused for another endpoint. In both cases, there's | |
159 // nothing more to do. | |
160 if (it == local_id_to_endpoint_map_.end() || it->second.get() != endpoint) | |
161 return; | |
162 | |
163 DCHECK(it->second); | |
164 it->second = nullptr; | |
165 | |
166 // Send a remove message outside the lock. | |
167 } | |
168 | |
169 if (!SendControlMessage(MessageInTransit::kSubtypeChannelRemoveEndpoint, | |
170 local_id, remote_id)) { | |
171 HandleLocalError(base::StringPrintf( | |
172 "Failed to send message to remove remote endpoint (local ID %u, remote " | |
173 "ID %u)", | |
174 static_cast<unsigned>(local_id.value()), | |
175 static_cast<unsigned>(remote_id.value()))); | |
176 } | |
177 } | |
178 | |
179 size_t Channel::GetSerializedEndpointSize() const { | |
180 return sizeof(SerializedEndpoint); | |
181 } | |
182 | |
183 void Channel::SerializeEndpointWithClosedPeer( | |
184 void* destination, | |
185 MessageInTransitQueue* message_queue) { | |
186 // We can actually just pass no client to |SerializeEndpointWithLocalPeer()|. | |
187 SerializeEndpointWithLocalPeer(destination, message_queue, nullptr, 0); | |
188 } | |
189 | |
190 scoped_refptr<ChannelEndpoint> Channel::SerializeEndpointWithLocalPeer( | |
191 void* destination, | |
192 MessageInTransitQueue* message_queue, | |
193 ChannelEndpointClient* endpoint_client, | |
194 unsigned endpoint_client_port) { | |
195 DCHECK(destination); | |
196 // Allow |endpoint_client| to be null, for use by | |
197 // |SerializeEndpointWithClosedPeer()|. | |
198 | |
199 scoped_refptr<ChannelEndpoint> endpoint(new ChannelEndpoint( | |
200 endpoint_client, endpoint_client_port, message_queue)); | |
201 | |
202 SerializedEndpoint* s = static_cast<SerializedEndpoint*>(destination); | |
203 s->receiver_endpoint_id = AttachAndRunEndpoint(endpoint); | |
204 DVLOG(2) << "Serializing endpoint with local or closed peer (remote ID = " | |
205 << s->receiver_endpoint_id << ")"; | |
206 | |
207 return endpoint; | |
208 } | |
209 | |
210 void Channel::SerializeEndpointWithRemotePeer( | |
211 void* destination, | |
212 MessageInTransitQueue* message_queue, | |
213 scoped_refptr<ChannelEndpoint> peer_endpoint) { | |
214 DCHECK(destination); | |
215 DCHECK(peer_endpoint); | |
216 | |
217 DLOG(WARNING) << "Direct message pipe passing across multiple channels not " | |
218 "yet implemented; will proxy"; | |
219 // Create and set up an |EndpointRelayer| to proxy. | |
220 // TODO(vtl): If we were to own/track the relayer directly (rather than owning | |
221 // it via its |ChannelEndpoint|s), then we might be able to make | |
222 // |ChannelEndpoint|'s |client_| pointer a raw pointer. | |
223 scoped_refptr<EndpointRelayer> relayer(new EndpointRelayer()); | |
224 scoped_refptr<ChannelEndpoint> endpoint( | |
225 new ChannelEndpoint(relayer.get(), 0, message_queue)); | |
226 relayer->Init(endpoint.get(), peer_endpoint.get()); | |
227 peer_endpoint->ReplaceClient(relayer.get(), 1); | |
228 | |
229 SerializedEndpoint* s = static_cast<SerializedEndpoint*>(destination); | |
230 s->receiver_endpoint_id = AttachAndRunEndpoint(endpoint); | |
231 DVLOG(2) << "Serializing endpoint with remote peer (remote ID = " | |
232 << s->receiver_endpoint_id << ")"; | |
233 } | |
234 | |
235 scoped_refptr<IncomingEndpoint> Channel::DeserializeEndpoint( | |
236 const void* source) { | |
237 const SerializedEndpoint* s = static_cast<const SerializedEndpoint*>(source); | |
238 ChannelEndpointId local_id = s->receiver_endpoint_id; | |
239 // No need to check the validity of |local_id| -- if it's not valid, it simply | |
240 // won't be in |incoming_endpoints_|. | |
241 DVLOG_IF(2, !local_id.is_valid() || !local_id.is_remote()) | |
242 << "Attempt to get incoming endpoint for invalid ID " << local_id; | |
243 | |
244 base::AutoLock locker(lock_); | |
245 | |
246 auto it = incoming_endpoints_.find(local_id); | |
247 if (it == incoming_endpoints_.end()) { | |
248 LOG(ERROR) << "Failed to deserialize endpoint (ID = " << local_id << ")"; | |
249 return nullptr; | |
250 } | |
251 | |
252 DVLOG(2) << "Deserializing endpoint (new local ID = " << local_id << ")"; | |
253 | |
254 scoped_refptr<IncomingEndpoint> rv; | |
255 rv.swap(it->second); | |
256 incoming_endpoints_.erase(it); | |
257 return rv; | |
258 } | |
259 | |
260 size_t Channel::GetSerializedPlatformHandleSize() const { | |
261 return raw_channel_->GetSerializedPlatformHandleSize(); | |
262 } | |
263 | |
264 Channel::~Channel() { | |
265 // The channel should have been shut down first. | |
266 DCHECK(!is_running_); | |
267 } | |
268 | |
269 void Channel::OnReadMessage( | |
270 const MessageInTransit::View& message_view, | |
271 embedder::ScopedPlatformHandleVectorPtr platform_handles) { | |
272 DCHECK(creation_thread_checker_.CalledOnValidThread()); | |
273 | |
274 switch (message_view.type()) { | |
275 case MessageInTransit::kTypeEndpoint: | |
276 OnReadMessageForEndpoint(message_view, platform_handles.Pass()); | |
277 break; | |
278 case MessageInTransit::kTypeChannel: | |
279 OnReadMessageForChannel(message_view, platform_handles.Pass()); | |
280 break; | |
281 default: | |
282 HandleRemoteError( | |
283 base::StringPrintf("Received message of invalid type %u", | |
284 static_cast<unsigned>(message_view.type()))); | |
285 break; | |
286 } | |
287 } | |
288 | |
289 void Channel::OnError(Error error) { | |
290 DCHECK(creation_thread_checker_.CalledOnValidThread()); | |
291 | |
292 switch (error) { | |
293 case ERROR_READ_SHUTDOWN: | |
294 // The other side was cleanly closed, so this isn't actually an error. | |
295 DVLOG(1) << "RawChannel read error (shutdown)"; | |
296 break; | |
297 case ERROR_READ_BROKEN: { | |
298 base::AutoLock locker(lock_); | |
299 LOG_IF(ERROR, !is_shutting_down_) | |
300 << "RawChannel read error (connection broken)"; | |
301 break; | |
302 } | |
303 case ERROR_READ_BAD_MESSAGE: | |
304 // Receiving a bad message means either a bug, data corruption, or | |
305 // malicious attack (probably due to some other bug). | |
306 LOG(ERROR) << "RawChannel read error (received bad message)"; | |
307 break; | |
308 case ERROR_READ_UNKNOWN: | |
309 LOG(ERROR) << "RawChannel read error (unknown)"; | |
310 break; | |
311 case ERROR_WRITE: | |
312 // Write errors are slightly notable: they probably shouldn't happen under | |
313 // normal operation (but maybe the other side crashed). | |
314 LOG(WARNING) << "RawChannel write error"; | |
315 break; | |
316 } | |
317 Shutdown(); | |
318 } | |
319 | |
320 void Channel::OnReadMessageForEndpoint( | |
321 const MessageInTransit::View& message_view, | |
322 embedder::ScopedPlatformHandleVectorPtr platform_handles) { | |
323 DCHECK(creation_thread_checker_.CalledOnValidThread()); | |
324 DCHECK(message_view.type() == MessageInTransit::kTypeEndpoint); | |
325 | |
326 ChannelEndpointId local_id = message_view.destination_id(); | |
327 if (!local_id.is_valid()) { | |
328 HandleRemoteError("Received message with no destination ID"); | |
329 return; | |
330 } | |
331 | |
332 scoped_refptr<ChannelEndpoint> endpoint; | |
333 { | |
334 base::AutoLock locker(lock_); | |
335 | |
336 // Since we own |raw_channel_|, and this method and |Shutdown()| should only | |
337 // be called from the creation thread, |raw_channel_| should never be null | |
338 // here. | |
339 DCHECK(is_running_); | |
340 | |
341 IdToEndpointMap::const_iterator it = | |
342 local_id_to_endpoint_map_.find(local_id); | |
343 if (it != local_id_to_endpoint_map_.end()) { | |
344 // Ignore messages for zombie endpoints (not an error). | |
345 if (!it->second) { | |
346 DVLOG(2) << "Ignoring downstream message for zombie endpoint (local ID " | |
347 "= " << local_id | |
348 << ", remote ID = " << message_view.source_id() << ")"; | |
349 return; | |
350 } | |
351 | |
352 endpoint = it->second; | |
353 } | |
354 } | |
355 if (!endpoint) { | |
356 HandleRemoteError(base::StringPrintf( | |
357 "Received a message for nonexistent local destination ID %u", | |
358 static_cast<unsigned>(local_id.value()))); | |
359 // This is strongly indicative of some problem. However, it's not a fatal | |
360 // error, since it may indicate a buggy (or hostile) remote process. Don't | |
361 // die even for Debug builds, since handling this properly needs to be | |
362 // tested (TODO(vtl)). | |
363 DLOG(ERROR) << "This should not happen under normal operation."; | |
364 return; | |
365 } | |
366 | |
367 scoped_ptr<MessageInTransit> message(new MessageInTransit(message_view)); | |
368 if (message_view.transport_data_buffer_size() > 0) { | |
369 DCHECK(message_view.transport_data_buffer()); | |
370 message->SetDispatchers(TransportData::DeserializeDispatchers( | |
371 message_view.transport_data_buffer(), | |
372 message_view.transport_data_buffer_size(), platform_handles.Pass(), | |
373 this)); | |
374 } | |
375 | |
376 endpoint->OnReadMessage(message.Pass()); | |
377 } | |
378 | |
379 void Channel::OnReadMessageForChannel( | |
380 const MessageInTransit::View& message_view, | |
381 embedder::ScopedPlatformHandleVectorPtr platform_handles) { | |
382 DCHECK(creation_thread_checker_.CalledOnValidThread()); | |
383 DCHECK_EQ(message_view.type(), MessageInTransit::kTypeChannel); | |
384 | |
385 // Currently, no channel messages take platform handles. | |
386 if (platform_handles) { | |
387 HandleRemoteError( | |
388 "Received invalid channel message (has platform handles)"); | |
389 NOTREACHED(); | |
390 return; | |
391 } | |
392 | |
393 switch (message_view.subtype()) { | |
394 case MessageInTransit::kSubtypeChannelAttachAndRunEndpoint: | |
395 DVLOG(2) << "Handling channel message to attach and run endpoint (local " | |
396 "ID " << message_view.destination_id() << ", remote ID " | |
397 << message_view.source_id() << ")"; | |
398 if (!OnAttachAndRunEndpoint(message_view.destination_id(), | |
399 message_view.source_id())) { | |
400 HandleRemoteError( | |
401 "Received invalid channel message to attach and run endpoint"); | |
402 } | |
403 break; | |
404 case MessageInTransit::kSubtypeChannelRemoveEndpoint: | |
405 DVLOG(2) << "Handling channel message to remove endpoint (local ID " | |
406 << message_view.destination_id() << ", remote ID " | |
407 << message_view.source_id() << ")"; | |
408 if (!OnRemoveEndpoint(message_view.destination_id(), | |
409 message_view.source_id())) { | |
410 HandleRemoteError( | |
411 "Received invalid channel message to remove endpoint"); | |
412 } | |
413 break; | |
414 case MessageInTransit::kSubtypeChannelRemoveEndpointAck: | |
415 DVLOG(2) << "Handling channel message to ack remove endpoint (local ID " | |
416 << message_view.destination_id() << ", remote ID " | |
417 << message_view.source_id() << ")"; | |
418 if (!OnRemoveEndpointAck(message_view.destination_id())) { | |
419 HandleRemoteError( | |
420 "Received invalid channel message to ack remove endpoint"); | |
421 } | |
422 break; | |
423 default: | |
424 HandleRemoteError("Received invalid channel message"); | |
425 NOTREACHED(); | |
426 break; | |
427 } | |
428 } | |
429 | |
430 bool Channel::OnAttachAndRunEndpoint(ChannelEndpointId local_id, | |
431 ChannelEndpointId remote_id) { | |
432 // We should only get this for remotely-created local endpoints, so our local | |
433 // ID should be "remote". | |
434 if (!local_id.is_valid() || !local_id.is_remote()) { | |
435 DVLOG(2) << "Received attach and run endpoint with invalid local ID"; | |
436 return false; | |
437 } | |
438 | |
439 // Conversely, the remote end should be "local". | |
440 if (!remote_id.is_valid() || remote_id.is_remote()) { | |
441 DVLOG(2) << "Received attach and run endpoint with invalid remote ID"; | |
442 return false; | |
443 } | |
444 | |
445 // Create/initialize an |IncomingEndpoint| and thus an endpoint (outside the | |
446 // lock). | |
447 scoped_refptr<IncomingEndpoint> incoming_endpoint(new IncomingEndpoint()); | |
448 scoped_refptr<ChannelEndpoint> endpoint = incoming_endpoint->Init(); | |
449 | |
450 bool success = true; | |
451 { | |
452 base::AutoLock locker(lock_); | |
453 | |
454 if (local_id_to_endpoint_map_.find(local_id) == | |
455 local_id_to_endpoint_map_.end()) { | |
456 DCHECK(incoming_endpoints_.find(local_id) == incoming_endpoints_.end()); | |
457 | |
458 // TODO(vtl): Use emplace when we move to C++11 unordered_maps. (It'll | |
459 // avoid some refcount churn.) | |
460 local_id_to_endpoint_map_[local_id] = endpoint; | |
461 incoming_endpoints_[local_id] = incoming_endpoint; | |
462 } else { | |
463 // We need to call |Close()| outside the lock. | |
464 success = false; | |
465 } | |
466 } | |
467 if (!success) { | |
468 DVLOG(2) << "Received attach and run endpoint for existing local ID"; | |
469 incoming_endpoint->Close(); | |
470 return false; | |
471 } | |
472 | |
473 endpoint->AttachAndRun(this, local_id, remote_id); | |
474 return true; | |
475 } | |
476 | |
477 bool Channel::OnRemoveEndpoint(ChannelEndpointId local_id, | |
478 ChannelEndpointId remote_id) { | |
479 DCHECK(creation_thread_checker_.CalledOnValidThread()); | |
480 | |
481 scoped_refptr<ChannelEndpoint> endpoint; | |
482 { | |
483 base::AutoLock locker(lock_); | |
484 | |
485 IdToEndpointMap::iterator it = local_id_to_endpoint_map_.find(local_id); | |
486 if (it == local_id_to_endpoint_map_.end()) { | |
487 DVLOG(2) << "Remove endpoint error: not found"; | |
488 return false; | |
489 } | |
490 | |
491 if (!it->second) { | |
492 // Remove messages "crossed"; we have to wait for the ack. | |
493 return true; | |
494 } | |
495 | |
496 endpoint = it->second; | |
497 local_id_to_endpoint_map_.erase(it); | |
498 // Detach and send the remove ack message outside the lock. | |
499 } | |
500 | |
501 endpoint->DetachFromChannel(); | |
502 | |
503 if (!SendControlMessage(MessageInTransit::kSubtypeChannelRemoveEndpointAck, | |
504 local_id, remote_id)) { | |
505 HandleLocalError(base::StringPrintf( | |
506 "Failed to send message to ack remove remote endpoint (local ID %u, " | |
507 "remote ID %u)", | |
508 static_cast<unsigned>(local_id.value()), | |
509 static_cast<unsigned>(remote_id.value()))); | |
510 } | |
511 | |
512 return true; | |
513 } | |
514 | |
515 bool Channel::OnRemoveEndpointAck(ChannelEndpointId local_id) { | |
516 DCHECK(creation_thread_checker_.CalledOnValidThread()); | |
517 | |
518 base::AutoLock locker(lock_); | |
519 | |
520 IdToEndpointMap::iterator it = local_id_to_endpoint_map_.find(local_id); | |
521 if (it == local_id_to_endpoint_map_.end()) { | |
522 DVLOG(2) << "Remove endpoint ack error: not found"; | |
523 return false; | |
524 } | |
525 | |
526 if (it->second) { | |
527 DVLOG(2) << "Remove endpoint ack error: wrong state"; | |
528 return false; | |
529 } | |
530 | |
531 local_id_to_endpoint_map_.erase(it); | |
532 return true; | |
533 } | |
534 | |
535 void Channel::HandleRemoteError(const base::StringPiece& error_message) { | |
536 // TODO(vtl): Is this how we really want to handle this? Probably we want to | |
537 // terminate the connection, since it's spewing invalid stuff. | |
538 LOG(WARNING) << error_message; | |
539 } | |
540 | |
541 void Channel::HandleLocalError(const base::StringPiece& error_message) { | |
542 // TODO(vtl): Is this how we really want to handle this? | |
543 // Sometimes we'll want to propagate the error back to the message pipe | |
544 // (endpoint), and notify it that the remote is (effectively) closed. | |
545 // Sometimes we'll want to kill the channel (and notify all the endpoints that | |
546 // their remotes are dead. | |
547 LOG(WARNING) << error_message; | |
548 } | |
549 | |
550 // Note: |endpoint| being a |scoped_refptr| makes this function safe, since it | |
551 // keeps the endpoint alive even after the lock is released. Otherwise, there's | |
552 // the temptation to simply pass the result of |new ChannelEndpoint(...)| | |
553 // directly to this function, which wouldn't be sufficient for safety. | |
554 ChannelEndpointId Channel::AttachAndRunEndpoint( | |
555 scoped_refptr<ChannelEndpoint> endpoint) { | |
556 DCHECK(endpoint); | |
557 | |
558 ChannelEndpointId local_id; | |
559 ChannelEndpointId remote_id; | |
560 { | |
561 base::AutoLock locker(lock_); | |
562 | |
563 DLOG_IF(WARNING, is_shutting_down_) | |
564 << "AttachAndRunEndpoint() while shutting down"; | |
565 | |
566 do { | |
567 local_id = local_id_generator_.GetNext(); | |
568 } while (local_id_to_endpoint_map_.find(local_id) != | |
569 local_id_to_endpoint_map_.end()); | |
570 | |
571 // TODO(vtl): We also need to check for collisions of remote IDs here. | |
572 remote_id = remote_id_generator_.GetNext(); | |
573 | |
574 local_id_to_endpoint_map_[local_id] = endpoint; | |
575 } | |
576 | |
577 if (!SendControlMessage(MessageInTransit::kSubtypeChannelAttachAndRunEndpoint, | |
578 local_id, remote_id)) { | |
579 HandleLocalError(base::StringPrintf( | |
580 "Failed to send message to run remote endpoint (local ID %u, remote ID " | |
581 "%u)", | |
582 static_cast<unsigned>(local_id.value()), | |
583 static_cast<unsigned>(remote_id.value()))); | |
584 // TODO(vtl): Should we continue on to |AttachAndRun()|? | |
585 } | |
586 | |
587 endpoint->AttachAndRun(this, local_id, remote_id); | |
588 return remote_id; | |
589 } | |
590 | |
591 bool Channel::SendControlMessage(MessageInTransit::Subtype subtype, | |
592 ChannelEndpointId local_id, | |
593 ChannelEndpointId remote_id) { | |
594 DVLOG(2) << "Sending channel control message: subtype " << subtype | |
595 << ", local ID " << local_id << ", remote ID " << remote_id; | |
596 scoped_ptr<MessageInTransit> message(new MessageInTransit( | |
597 MessageInTransit::kTypeChannel, subtype, 0, nullptr)); | |
598 message->set_source_id(local_id); | |
599 message->set_destination_id(remote_id); | |
600 return WriteMessage(message.Pass()); | |
601 } | |
602 | |
603 } // namespace system | |
604 } // namespace mojo | |
OLD | NEW |