1 // Copyright (c) 2012 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.
6 #include "base/compiler_specific.h"
7 #include "base/debug/trace_event.h"
8 #include "base/location.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "ipc/ipc_channel_proxy.h"
14 #include "ipc/ipc_listener.h"
15 #include "ipc/ipc_logging.h"
16 #include "ipc/ipc_message_macros.h"
17 #include "ipc/ipc_message_utils.h"
21 //------------------------------------------------------------------------------
23 ChannelProxy::MessageFilter::MessageFilter() {}
25 void ChannelProxy::MessageFilter::OnFilterAdded(Channel
* channel
) {}
27 void ChannelProxy::MessageFilter::OnFilterRemoved() {}
29 void ChannelProxy::MessageFilter::OnChannelConnected(int32 peer_pid
) {}
31 void ChannelProxy::MessageFilter::OnChannelError() {}
33 void ChannelProxy::MessageFilter::OnChannelClosing() {}
35 bool ChannelProxy::MessageFilter::OnMessageReceived(const Message
& message
) {
39 void ChannelProxy::MessageFilter::OnDestruct() const {
43 ChannelProxy::MessageFilter::~MessageFilter() {}
45 //------------------------------------------------------------------------------
47 ChannelProxy::Context::Context(Listener
* listener
,
48 base::SingleThreadTaskRunner
* ipc_task_runner
)
49 : listener_task_runner_(base::ThreadTaskRunnerHandle::Get()),
51 ipc_task_runner_(ipc_task_runner
),
52 channel_connected_called_(false),
53 peer_pid_(base::kNullProcessId
) {
54 DCHECK(ipc_task_runner_
);
57 ChannelProxy::Context::~Context() {
60 void ChannelProxy::Context::ClearIPCTaskRunner() {
61 ipc_task_runner_
= NULL
;
64 void ChannelProxy::Context::CreateChannel(const IPC::ChannelHandle
& handle
,
65 const Channel::Mode
& mode
) {
66 DCHECK(channel_
.get() == NULL
);
67 channel_id_
= handle
.name
;
68 channel_
.reset(new Channel(handle
, mode
, this));
71 bool ChannelProxy::Context::TryFilters(const Message
& message
) {
72 #ifdef IPC_MESSAGE_LOG_ENABLED
73 Logging
* logger
= Logging::GetInstance();
74 if (logger
->Enabled())
75 logger
->OnPreDispatchMessage(message
);
78 for (size_t i
= 0; i
< filters_
.size(); ++i
) {
79 if (filters_
[i
]->OnMessageReceived(message
)) {
80 #ifdef IPC_MESSAGE_LOG_ENABLED
81 if (logger
->Enabled())
82 logger
->OnPostDispatchMessage(message
, channel_id_
);
90 // Called on the IPC::Channel thread
91 bool ChannelProxy::Context::OnMessageReceived(const Message
& message
) {
92 // First give a chance to the filters to process this message.
93 if (!TryFilters(message
))
94 OnMessageReceivedNoFilter(message
);
98 // Called on the IPC::Channel thread
99 bool ChannelProxy::Context::OnMessageReceivedNoFilter(const Message
& message
) {
100 // NOTE: This code relies on the listener's message loop not going away while
101 // this thread is active. That should be a reasonable assumption, but it
102 // feels risky. We may want to invent some more indirect way of referring to
103 // a MessageLoop if this becomes a problem.
104 listener_task_runner_
->PostTask(
105 FROM_HERE
, base::Bind(&Context::OnDispatchMessage
, this, message
));
109 // Called on the IPC::Channel thread
110 void ChannelProxy::Context::OnChannelConnected(int32 peer_pid
) {
111 // Add any pending filters. This avoids a race condition where someone
112 // creates a ChannelProxy, calls AddFilter, and then right after starts the
113 // peer process. The IO thread could receive a message before the task to add
114 // the filter is run on the IO thread.
117 // We cache off the peer_pid so it can be safely accessed from both threads.
118 peer_pid_
= channel_
->peer_pid();
119 for (size_t i
= 0; i
< filters_
.size(); ++i
)
120 filters_
[i
]->OnChannelConnected(peer_pid
);
122 // See above comment about using listener_task_runner_ here.
123 listener_task_runner_
->PostTask(
124 FROM_HERE
, base::Bind(&Context::OnDispatchConnected
, this));
127 // Called on the IPC::Channel thread
128 void ChannelProxy::Context::OnChannelError() {
129 for (size_t i
= 0; i
< filters_
.size(); ++i
)
130 filters_
[i
]->OnChannelError();
132 // See above comment about using listener_task_runner_ here.
133 listener_task_runner_
->PostTask(
134 FROM_HERE
, base::Bind(&Context::OnDispatchError
, this));
137 // Called on the IPC::Channel thread
138 void ChannelProxy::Context::OnChannelOpened() {
139 DCHECK(channel_
!= NULL
);
141 // Assume a reference to ourselves on behalf of this thread. This reference
142 // will be released when we are closed.
145 if (!channel_
->Connect()) {
150 for (size_t i
= 0; i
< filters_
.size(); ++i
)
151 filters_
[i
]->OnFilterAdded(channel_
.get());
154 // Called on the IPC::Channel thread
155 void ChannelProxy::Context::OnChannelClosed() {
156 // It's okay for IPC::ChannelProxy::Close to be called more than once, which
157 // would result in this branch being taken.
161 for (size_t i
= 0; i
< filters_
.size(); ++i
) {
162 filters_
[i
]->OnChannelClosing();
163 filters_
[i
]->OnFilterRemoved();
166 // We don't need the filters anymore.
171 // Balance with the reference taken during startup. This may result in
176 void ChannelProxy::Context::Clear() {
180 // Called on the IPC::Channel thread
181 void ChannelProxy::Context::OnSendMessage(scoped_ptr
<Message
> message
) {
182 if (!channel_
.get()) {
186 if (!channel_
->Send(message
.release()))
190 // Called on the IPC::Channel thread
191 void ChannelProxy::Context::OnAddFilter() {
192 std::vector
<scoped_refptr
<MessageFilter
> > new_filters
;
194 base::AutoLock
auto_lock(pending_filters_lock_
);
195 new_filters
.swap(pending_filters_
);
198 for (size_t i
= 0; i
< new_filters
.size(); ++i
) {
199 filters_
.push_back(new_filters
[i
]);
201 // If the channel has already been created, then we need to send this
202 // message so that the filter gets access to the Channel.
204 new_filters
[i
]->OnFilterAdded(channel_
.get());
205 // Ditto for if the channel has been connected.
207 new_filters
[i
]->OnChannelConnected(peer_pid_
);
211 // Called on the IPC::Channel thread
212 void ChannelProxy::Context::OnRemoveFilter(MessageFilter
* filter
) {
213 for (size_t i
= 0; i
< filters_
.size(); ++i
) {
214 if (filters_
[i
].get() == filter
) {
215 filter
->OnFilterRemoved();
216 filters_
.erase(filters_
.begin() + i
);
221 NOTREACHED() << "filter to be removed not found";
224 // Called on the listener's thread
225 void ChannelProxy::Context::AddFilter(MessageFilter
* filter
) {
226 base::AutoLock
auto_lock(pending_filters_lock_
);
227 pending_filters_
.push_back(make_scoped_refptr(filter
));
228 ipc_task_runner_
->PostTask(
229 FROM_HERE
, base::Bind(&Context::OnAddFilter
, this));
232 // Called on the listener's thread
233 void ChannelProxy::Context::OnDispatchMessage(const Message
& message
) {
234 #ifdef IPC_MESSAGE_LOG_ENABLED
235 Logging
* logger
= Logging::GetInstance();
237 logger
->GetMessageText(message
.type(), &name
, &message
, NULL
);
238 TRACE_EVENT1("task", "ChannelProxy::Context::OnDispatchMessage",
241 TRACE_EVENT2("task", "ChannelProxy::Context::OnDispatchMessage",
242 "class", IPC_MESSAGE_ID_CLASS(message
.type()),
243 "line", IPC_MESSAGE_ID_LINE(message
.type()));
249 OnDispatchConnected();
251 #ifdef IPC_MESSAGE_LOG_ENABLED
252 if (message
.type() == IPC_LOGGING_ID
) {
253 logger
->OnReceivedLoggingMessage(message
);
257 if (logger
->Enabled())
258 logger
->OnPreDispatchMessage(message
);
261 listener_
->OnMessageReceived(message
);
263 #ifdef IPC_MESSAGE_LOG_ENABLED
264 if (logger
->Enabled())
265 logger
->OnPostDispatchMessage(message
, channel_id_
);
269 // Called on the listener's thread
270 void ChannelProxy::Context::OnDispatchConnected() {
271 if (channel_connected_called_
)
274 channel_connected_called_
= true;
276 listener_
->OnChannelConnected(peer_pid_
);
279 // Called on the listener's thread
280 void ChannelProxy::Context::OnDispatchError() {
282 listener_
->OnChannelError();
285 //-----------------------------------------------------------------------------
287 ChannelProxy::ChannelProxy(const IPC::ChannelHandle
& channel_handle
,
290 base::SingleThreadTaskRunner
* ipc_task_runner
)
291 : context_(new Context(listener
, ipc_task_runner
)),
292 outgoing_message_filter_(NULL
),
294 Init(channel_handle
, mode
, true);
297 ChannelProxy::ChannelProxy(Context
* context
)
299 outgoing_message_filter_(NULL
),
303 ChannelProxy::~ChannelProxy() {
304 DCHECK(CalledOnValidThread());
309 void ChannelProxy::Init(const IPC::ChannelHandle
& channel_handle
,
311 bool create_pipe_now
) {
312 DCHECK(CalledOnValidThread());
314 #if defined(OS_POSIX)
315 // When we are creating a server on POSIX, we need its file descriptor
316 // to be created immediately so that it can be accessed and passed
317 // to other processes. Forcing it to be created immediately avoids
318 // race conditions that may otherwise arise.
319 if (mode
& Channel::MODE_SERVER_FLAG
) {
320 create_pipe_now
= true;
322 #endif // defined(OS_POSIX)
324 if (create_pipe_now
) {
325 // Create the channel immediately. This effectively sets up the
326 // low-level pipe so that the client can connect. Without creating
327 // the pipe immediately, it is possible for a listener to attempt
328 // to connect and get an error since the pipe doesn't exist yet.
329 context_
->CreateChannel(channel_handle
, mode
);
331 context_
->ipc_task_runner()->PostTask(
332 FROM_HERE
, base::Bind(&Context::CreateChannel
, context_
.get(),
333 channel_handle
, mode
));
336 // complete initialization on the background thread
337 context_
->ipc_task_runner()->PostTask(
338 FROM_HERE
, base::Bind(&Context::OnChannelOpened
, context_
.get()));
343 void ChannelProxy::Close() {
344 DCHECK(CalledOnValidThread());
346 // Clear the backpointer to the listener so that any pending calls to
347 // Context::OnDispatchMessage or OnDispatchError will be ignored. It is
348 // possible that the channel could be closed while it is receiving messages!
351 if (context_
->ipc_task_runner()) {
352 context_
->ipc_task_runner()->PostTask(
353 FROM_HERE
, base::Bind(&Context::OnChannelClosed
, context_
.get()));
357 bool ChannelProxy::Send(Message
* message
) {
360 // TODO(alexeypa): add DCHECK(CalledOnValidThread()) here. Currently there are
361 // tests that call Send() from a wrong thread. See http://crbug.com/163523.
362 if (outgoing_message_filter())
363 message
= outgoing_message_filter()->Rewrite(message
);
365 #ifdef IPC_MESSAGE_LOG_ENABLED
366 Logging::GetInstance()->OnSendMessage(message
, context_
->channel_id());
369 context_
->ipc_task_runner()->PostTask(
371 base::Bind(&ChannelProxy::Context::OnSendMessage
,
372 context_
, base::Passed(scoped_ptr
<Message
>(message
))));
376 void ChannelProxy::AddFilter(MessageFilter
* filter
) {
377 DCHECK(CalledOnValidThread());
379 context_
->AddFilter(filter
);
382 void ChannelProxy::RemoveFilter(MessageFilter
* filter
) {
383 DCHECK(CalledOnValidThread());
385 context_
->ipc_task_runner()->PostTask(
386 FROM_HERE
, base::Bind(&Context::OnRemoveFilter
, context_
.get(),
387 make_scoped_refptr(filter
)));
390 void ChannelProxy::ClearIPCTaskRunner() {
391 DCHECK(CalledOnValidThread());
393 context()->ClearIPCTaskRunner();
396 #if defined(OS_POSIX) && !defined(OS_NACL)
397 // See the TODO regarding lazy initialization of the channel in
398 // ChannelProxy::Init().
399 int ChannelProxy::GetClientFileDescriptor() {
400 DCHECK(CalledOnValidThread());
402 Channel
* channel
= context_
.get()->channel_
.get();
403 // Channel must have been created first.
404 DCHECK(channel
) << context_
.get()->channel_id_
;
405 return channel
->GetClientFileDescriptor();
408 int ChannelProxy::TakeClientFileDescriptor() {
409 DCHECK(CalledOnValidThread());
411 Channel
* channel
= context_
.get()->channel_
.get();
412 // Channel must have been created first.
413 DCHECK(channel
) << context_
.get()->channel_id_
;
414 return channel
->TakeClientFileDescriptor();
417 bool ChannelProxy::GetPeerEuid(uid_t
* peer_euid
) const {
418 DCHECK(CalledOnValidThread());
420 Channel
* channel
= context_
.get()->channel_
.get();
421 // Channel must have been created first.
422 DCHECK(channel
) << context_
.get()->channel_id_
;
423 return channel
->GetPeerEuid(peer_euid
);
427 //-----------------------------------------------------------------------------