Fix leaking file descriptor when a new app is launched
[platform/framework/web/crosswalk.git] / src / xwalk / application / browser / linux / running_application_object.cc
1 // Copyright (c) 2013 Intel Corporation. All rights reserved.
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 #include "xwalk/application/browser/linux/running_application_object.h"
7
8 #include <string>
9 #include "base/values.h"
10 #include "base/bind.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "dbus/bus.h"
13 #include "dbus/message.h"
14 #include "dbus/exported_object.h"
15 #include "xwalk/application/browser/application_tizen.h"
16 #include "xwalk/application/browser/linux/running_applications_manager.h"
17
18 namespace {
19
20 // D-Bus Interface implemented by objects that represent running
21 // applications.
22 //
23 // Methods:
24 //
25 //   Terminate()
26 //     Will terminate the running application. This object will be unregistered
27 //     from D-Bus.
28 //
29 // Properties:
30 //
31 //   readonly string AppID
32 const char kRunningApplicationDBusInterface[] =
33     "org.crosswalkproject.Running.Application1";
34
35 const char kRunningApplicationDBusError[] =
36     "org.crosswalkproject.Running.Application.Error";
37
38
39 }  // namespace
40
41 namespace xwalk {
42 namespace application {
43
44 RunningApplicationObject::RunningApplicationObject(
45     scoped_refptr<dbus::Bus> bus,
46     const std::string& app_id,
47     const std::string& launcher_name,
48     Application* application)
49     : dbus::ManagedObject(bus, GetRunningPathForAppID(app_id)),
50       bus_(bus),
51       launcher_name_(launcher_name),
52       application_(application) {
53   ListenForOwnerChange();
54
55   properties()->Set(
56       kRunningApplicationDBusInterface, "AppID",
57       scoped_ptr<base::Value>(new base::StringValue(app_id)));
58
59   // FIXME: RemoveAllCookies and SetUserAgentString
60   // are exported for web_setting extension usage.
61   // This is a temporary solution - when another
62   // IPC on extension process side is implemented,
63   // these methods have to be removed.
64   dbus_object()->ExportMethod(
65       kRunningApplicationDBusInterface, "Terminate",
66       base::Bind(&RunningApplicationObject::OnTerminate,
67                  base::Unretained(this)),
68       base::Bind(&RunningApplicationObject::OnExported,
69                  base::Unretained(this)));
70
71   dbus_object()->ExportMethod(
72       kRunningApplicationDBusInterface, "GetEPChannel",
73       base::Bind(&RunningApplicationObject::OnGetExtensionProcessChannel,
74                  base::Unretained(this)),
75       base::Bind(&RunningApplicationObject::OnExported,
76                  base::Unretained(this)));
77
78 #if defined(OS_TIZEN)
79   dbus_object()->ExportMethod(
80       kRunningApplicationDBusInterface, "Hide",
81       base::Bind(&RunningApplicationObject::OnHide,
82                  base::Unretained(this)),
83       base::Bind(&RunningApplicationObject::OnExported,
84                  base::Unretained(this)));
85
86   dbus_object()->ExportMethod(
87       kRunningApplicationDBusInterface, "Suspend",
88       base::Bind(&RunningApplicationObject::OnSuspend,
89                  base::Unretained(this)),
90       base::Bind(&RunningApplicationObject::OnExported,
91                  base::Unretained(this)));
92
93   dbus_object()->ExportMethod(
94       kRunningApplicationDBusInterface, "Resume",
95       base::Bind(&RunningApplicationObject::OnResume,
96                  base::Unretained(this)),
97       base::Bind(&RunningApplicationObject::OnExported,
98                  base::Unretained(this)));
99
100   dbus_object()->ExportMethod(
101       kRunningApplicationDBusInterface, "RemoveAllCookies",
102       base::Bind(&RunningApplicationObject::OnRemoveAllCookies,
103                  base::Unretained(this)),
104       base::Bind(&RunningApplicationObject::OnExported,
105                  base::Unretained(this)));
106
107   dbus_object()->ExportMethod(
108       kRunningApplicationDBusInterface, "SetUserAgentString",
109       base::Bind(&RunningApplicationObject::OnSetUserAgentString,
110                  base::Unretained(this)),
111       base::Bind(&RunningApplicationObject::OnExported,
112                  base::Unretained(this)));
113 #endif
114 }
115
116 RunningApplicationObject::~RunningApplicationObject() {
117   UnlistenForOwnerChange();
118 }
119
120 void RunningApplicationObject::TerminateApplication() {
121   application_->Terminate();
122
123   if (ep_bp_channel_.socket.fd != -1)
124     close(ep_bp_channel_.socket.fd);
125 }
126
127 void RunningApplicationObject::OnExported(const std::string& interface_name,
128                                           const std::string& method_name,
129                                           bool success) {
130   if (!success) {
131     LOG(WARNING) << "Error exporting method '" << interface_name
132                  << "." << method_name << "' in '"
133                  << path().value() << "'.";
134   }
135 }
136
137 void RunningApplicationObject::OnTerminate(
138     dbus::MethodCall* method_call,
139     dbus::ExportedObject::ResponseSender response_sender) {
140   // We only allow the caller of Launch() to call Terminate().
141   if (method_call->GetSender() != launcher_name_) {
142     scoped_ptr<dbus::ErrorResponse> error_response =
143         dbus::ErrorResponse::FromMethodCall(method_call,
144                                             kRunningApplicationDBusError,
145                                             "Not permitted");
146     response_sender.Run(error_response.Pass());
147     return;
148   }
149
150   TerminateApplication();
151
152   scoped_ptr<dbus::Response> response =
153       dbus::Response::FromMethodCall(method_call);
154   response_sender.Run(response.Pass());
155 }
156
157 void RunningApplicationObject::OnGetExtensionProcessChannel(
158     dbus::MethodCall* method_call,
159     dbus::ExportedObject::ResponseSender response_sender) {
160   content::BrowserThread::PostTask(
161       content::BrowserThread::FILE,
162       FROM_HERE,
163       base::Bind(&RunningApplicationObject::SendChannel,
164                  base::Unretained(this),
165                  method_call,
166                  response_sender));
167 }
168
169 #if defined(OS_TIZEN)
170 void RunningApplicationObject::OnHide(
171     dbus::MethodCall* method_call,
172     dbus::ExportedObject::ResponseSender response_sender) {
173   if (method_call->GetSender() != launcher_name_) {
174     scoped_ptr<dbus::ErrorResponse> error_response =
175         dbus::ErrorResponse::FromMethodCall(method_call,
176                                             kRunningApplicationDBusError,
177                                             "Not permitted");
178     response_sender.Run(error_response.Pass());
179     return;
180   }
181
182   ToApplicationTizen(application_)->Hide();
183
184   scoped_ptr<dbus::Response> response =
185       dbus::Response::FromMethodCall(method_call);
186   response_sender.Run(response.Pass());
187 }
188
189 void RunningApplicationObject::OnSuspend(
190     dbus::MethodCall* method_call,
191     dbus::ExportedObject::ResponseSender response_sender) {
192   if (method_call->GetSender() != launcher_name_) {
193     scoped_ptr<dbus::ErrorResponse> error_response =
194         dbus::ErrorResponse::FromMethodCall(method_call,
195                                             kRunningApplicationDBusError,
196                                             "Not permitted");
197     response_sender.Run(error_response.Pass());
198     return;
199   }
200
201   ToApplicationTizen(application_)->Suspend();
202
203   scoped_ptr<dbus::Response> response =
204       dbus::Response::FromMethodCall(method_call);
205   response_sender.Run(response.Pass());
206 }
207
208 void RunningApplicationObject::OnResume(
209     dbus::MethodCall* method_call,
210     dbus::ExportedObject::ResponseSender response_sender) {
211   if (method_call->GetSender() != launcher_name_) {
212     scoped_ptr<dbus::ErrorResponse> error_response =
213         dbus::ErrorResponse::FromMethodCall(method_call,
214                                             kRunningApplicationDBusError,
215                                             "Not permitted");
216     response_sender.Run(error_response.Pass());
217     return;
218   }
219
220   ToApplicationTizen(application_)->Resume();
221   ToApplicationTizen(application_)->Show();
222
223   scoped_ptr<dbus::Response> response =
224       dbus::Response::FromMethodCall(method_call);
225   response_sender.Run(response.Pass());
226 }
227
228 void RunningApplicationObject::OnRemoveAllCookies(dbus::MethodCall* method_call,
229     dbus::ExportedObject::ResponseSender response_sender) {
230   if (method_call->GetSender() != launcher_name_) {
231     scoped_ptr<dbus::ErrorResponse> error_response =
232         dbus::ErrorResponse::FromMethodCall(method_call,
233                                             kRunningApplicationDBusError,
234                                             "Not permitted");
235     response_sender.Run(error_response.Pass());
236     return;
237   }
238
239   ToApplicationTizen(application_)->RemoveAllCookies();
240
241   scoped_ptr<dbus::Response> response =
242       dbus::Response::FromMethodCall(method_call);
243   response_sender.Run(response.Pass());
244 }
245
246 void RunningApplicationObject::SetUserAgentStringOnIOThread(
247     const std::string& user_agent_string) {
248   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
249   ToApplicationTizen(application_)->SetUserAgentString(user_agent_string);
250 }
251
252 void RunningApplicationObject::OnSetUserAgentString(
253     dbus::MethodCall* method_call,
254     dbus::ExportedObject::ResponseSender response_sender) {
255   if (method_call->GetSender() != launcher_name_) {
256     scoped_ptr<dbus::ErrorResponse> error_response =
257         dbus::ErrorResponse::FromMethodCall(method_call,
258                                             kRunningApplicationDBusError,
259                                             "Not permitted");
260     response_sender.Run(error_response.Pass());
261     return;
262   }
263   dbus::MessageReader reader(method_call);
264   std::string new_user_agent;
265   if (reader.PopString(&new_user_agent)) {
266     content::BrowserThread::PostTask(
267         content::BrowserThread::IO, FROM_HERE,
268         base::Bind(&RunningApplicationObject::SetUserAgentStringOnIOThread,
269                    base::Unretained(this), new_user_agent));
270     scoped_ptr<dbus::Response> response =
271         dbus::Response::FromMethodCall(method_call);
272     response_sender.Run(response.Pass());
273   } else {
274     scoped_ptr<dbus::ErrorResponse> error_response =
275         dbus::ErrorResponse::FromMethodCall(method_call,
276                                             kRunningApplicationDBusError,
277                                             "Wrong user agent string");
278     response_sender.Run(error_response.Pass());
279   }
280 }
281 #endif
282
283 void RunningApplicationObject::ListenForOwnerChange() {
284   owner_change_callback_ =
285       base::Bind(&RunningApplicationObject::OnNameOwnerChanged,
286                  base::Unretained(this));
287   bus_->ListenForServiceOwnerChange(launcher_name_, owner_change_callback_);
288 }
289
290 void RunningApplicationObject::UnlistenForOwnerChange() {
291   if (owner_change_callback_.is_null())
292     return;
293   bus_->UnlistenForServiceOwnerChange(launcher_name_, owner_change_callback_);
294   owner_change_callback_.Reset();
295 }
296
297 void RunningApplicationObject::OnNameOwnerChanged(
298     const std::string& service_owner) {
299   if (service_owner.empty()) {
300     // The process that sent the 'Launch' message has exited the session bus,
301     // we should kill the Running Application.
302     OnLauncherDisappeared();
303   }
304 }
305
306 void RunningApplicationObject::OnLauncherDisappeared() {
307   TerminateApplication();
308 }
309
310 void RunningApplicationObject::SendChannel(
311     dbus::MethodCall* method_call,
312     dbus::ExportedObject::ResponseSender response_sender) {
313   scoped_ptr<dbus::Response> response =
314       dbus::Response::FromMethodCall(method_call);
315
316   int fd = ep_bp_channel_.socket.fd;
317   if (fd == -1) {  // EP was not yet created, return empty response.
318     response_sender.Run(response.Pass());
319     return;
320   }
321
322   dbus::MessageWriter writer(response.get());
323   writer.AppendString(ep_bp_channel_.name);
324
325   scoped_ptr<dbus::FileDescriptor> client_fd(new dbus::FileDescriptor(fd));
326   client_fd->CheckValidity();
327   CHECK(client_fd->is_valid());
328   writer.AppendFileDescriptor(*client_fd);
329
330   response_sender.Run(response.Pass());
331 }
332
333 void RunningApplicationObject::ExtensionProcessCreated(
334     const IPC::ChannelHandle& handle) {
335   ep_bp_channel_ = handle;
336   dbus::Signal signal(kRunningApplicationDBusInterface, "EPChannelCreated");
337   dbus_object()->SendSignal(&signal);
338 }
339
340 }  // namespace application
341 }  // namespace xwalk