Merge branch 'tizen' into tizen_5.5
[platform/core/api/notification.git] / notification-ex / dbus_event_listener.cc
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <dlog.h>
18 #include <glib.h>
19 #include <unistd.h>
20
21 #include <string>
22 #include <list>
23
24 #include "notification-ex/dbus_connection_manager.h"
25 #include "notification-ex/dbus_event_listener.h"
26 #include "notification-ex/dbus_event_listener_implementation.h"
27 #include "notification-ex/exception.h"
28 #include "notification-ex/ex_util.h"
29 #include "notification-ex/event_info_internal.h"
30
31 #ifdef LOG_TAG
32 #undef LOG_TAG
33 #endif
34
35 #define LOG_TAG "NOTIFICATION_EX"
36 #define MAX_PACKAGE_STR_SIZE 512
37 #define NORMAL_UID_BASE 5000
38
39 #define DBUS_NAME "org.freedesktop.DBus"
40 #define DBUS_OBJECT_PATH "/org/freedesktop/DBus"
41 #define DBUS_INTERFACE_NAME "org.freedesktop.DBus"
42
43 using namespace std;
44 using namespace tizen_base;
45
46 namespace notification {
47
48 DBusEventListener::DBusEventListener(string path)
49     : impl_(new Impl(this, path)) {
50   LOGW("Created (%s)", path.c_str());
51 }
52
53 DBusEventListener::~DBusEventListener() {
54   LOGW("Destroyed");
55 }
56
57 DBusEventListener::Impl::~Impl() = default;
58
59 DBusEventListener::Impl::Impl(DBusEventListener* parent, string path)
60     : subscribe_id_(0), registration_id_(0), path_(path), parent_(parent) {
61   LOGI("Dbus_event_listener impl created");
62 }
63
64 uid_t DBusEventListener::Impl::GetSenderUid(GDBusConnection* connection,
65                 const char* sender_name) {
66   GDBusMessage* msg = nullptr;
67   GDBusMessage* reply = nullptr;
68   GError* err = nullptr;
69   GVariant* body;
70   uid_t uid = 0;
71
72   msg = g_dbus_message_new_method_call(DBUS_NAME, DBUS_OBJECT_PATH,
73       DBUS_INTERFACE_NAME, "GetConnectionUnixUser");
74   if (!msg) {
75     LOGE("Failed to alloc new method call");
76     goto out;
77   }
78
79   g_dbus_message_set_body(msg, g_variant_new("(s)", sender_name));
80   reply = g_dbus_connection_send_message_with_reply_sync(connection, msg,
81       G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, nullptr, nullptr, &err);
82
83   if (!reply) {
84     if (err != nullptr) {
85       LOGE("Failed to get uid [%s]", err->message);
86       g_error_free(err);
87     }
88     goto out;
89   }
90
91   body = g_dbus_message_get_body(reply);
92   g_variant_get(body, "(u)", &uid);
93
94 out:
95   if (msg)
96     g_object_unref(msg);
97   if (reply)
98     g_object_unref(reply);
99
100   return uid;
101 }
102
103 pid_t DBusEventListener::Impl::GetSenderPid(GDBusConnection* connection,
104                const char* sender_name) {
105   GDBusMessage* msg = nullptr;
106   GDBusMessage* reply = nullptr;
107   GError* err = nullptr;
108   GVariant* body;
109   pid_t pid = 0;
110
111   msg = g_dbus_message_new_method_call(DBUS_NAME, DBUS_OBJECT_PATH,
112       DBUS_INTERFACE_NAME, "GetConnectionUnixProcessID");
113   if (!msg) {
114     LOGE("Failed to alloc new method call");
115     goto out;
116   }
117
118   g_dbus_message_set_body(msg, g_variant_new("(s)", sender_name));
119   reply = g_dbus_connection_send_message_with_reply_sync(connection, msg,
120       G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, nullptr, nullptr, &err);
121
122   if (!reply) {
123     if (err != nullptr) {
124       LOGE("Failed to get uid [%s]", err->message);
125       g_error_free(err);
126     }
127     goto out;
128   }
129
130   body = g_dbus_message_get_body(reply);
131   g_variant_get(body, "(u)", &pid);
132
133 out:
134   if (msg)
135     g_object_unref(msg);
136   if (reply)
137     g_object_unref(reply);
138
139   return pid;
140 }
141
142 void DBusEventListener::Impl::SignalCb(GDBusConnection* connection,
143                       const gchar* sender_name,
144                       const gchar* object_path,
145                       const gchar* interface_name,
146                       const gchar* signal_name,
147                       GVariant* parameters,
148                       void* user_data) {
149   DBusEventListener::Impl* dl = static_cast<DBusEventListener::Impl*>(user_data);
150   char* appid = nullptr;
151   GVariantIter *iter = nullptr;
152   char* event_info_raw = nullptr;
153   g_variant_get(parameters, "(ssa(s))", &appid, &event_info_raw, &iter);
154
155   LOGI("signal callback!! (%s)", appid);
156   string sender_appid = string(appid);
157   string cur_appid = util::GetAppId();
158   if (sender_appid == cur_appid)
159     return;
160   if ((!DBusConnectionManager::GetInst().IsDataProviderMaster(cur_appid)
161     && !DBusConnectionManager::GetInst().IsDataProviderMaster(sender_appid))
162     || (cur_appid == sender_appid))
163     return;
164
165   LOGE("%s : %s", cur_appid.c_str(), sender_appid.c_str());
166
167   char* raw = nullptr;
168   list<Bundle> ret_list;
169   while (g_variant_iter_loop(iter, "(s)", &raw) && raw != nullptr) {
170     Bundle ret(raw);
171     ret_list.emplace_back(ret);
172   }
173
174   Bundle b(event_info_raw);
175   EventInfo info(b);
176
177   if (info.GetEventType() == EventInfo::Post
178            || info.GetEventType() == EventInfo::Update) {
179     uid_t uid = GetSenderUid(connection, sender_name);
180     if (uid >= NORMAL_UID_BASE) {
181       pid_t pid = GetSenderPid(connection, sender_name);
182       info.SetValidatedOwner(util::GetAppId(pid));
183       info.SetValidatedUid(uid);
184     }
185   }
186
187   dl->parent_->NotifyObserver(info, ret_list);
188 }
189
190 void DBusEventListener::Impl::OnMethodCall(
191     GDBusConnection *conn, const gchar *sender, const gchar *object_path,
192     const gchar *iface_name, const gchar *method_name,
193     GVariant *parameters, GDBusMethodInvocation *invocation,
194     gpointer user_data) {
195   LOGI("method_name[%s] sender[%s]", method_name, sender);
196   DBusEventListener::Impl* dl = static_cast<DBusEventListener::Impl*>(user_data);
197   GVariant *reply_body = NULL;
198   if (g_strcmp0(method_name, "Get") == 0) {
199     char* appid = NULL;
200     char* serialized = NULL;
201     g_variant_get(parameters, "(&s&s)", &appid, &serialized);
202
203     Bundle b(serialized);
204     EventInfo info(b);
205     list<Bundle> result = dl->parent_->NotifyObserver(info);
206     GVariantBuilder* builder = g_variant_builder_new(G_VARIANT_TYPE("a(s)"));
207     for (auto& i : result) {
208       g_variant_builder_add(builder, "(s)",
209           reinterpret_cast<char*>(i.ToRaw().first.get()));
210     }
211     reply_body = g_variant_new("(a(s))", builder);
212     g_variant_builder_unref(builder);
213   } else if (g_strcmp0(method_name, "Count") == 0) {
214     char* appid = NULL;
215     char* serialized = NULL;
216     g_variant_get(parameters, "(&s&s)", &appid, &serialized);
217
218     Bundle b(serialized);
219     EventInfo info(b);
220     int num = dl->parent_->NotifyNumberRequest(info);
221     reply_body = g_variant_new("(i)", num);
222   }
223   g_dbus_method_invocation_return_value(invocation, reply_body);
224 }
225
226 int DBusEventListener::Impl::RegisterGDBusInterface() {
227   static const GDBusInterfaceVTable interface_vtable = {
228     OnMethodCall,
229     nullptr,
230     nullptr
231   };
232   static gchar introspection_xml[] =
233     "  <node>"
234     "  <interface name='org.tizen.notification_ex'>"
235     "        <method name='Get'>"
236     "          <arg type='s' name='appid' direction='in'/>"
237     "          <arg type='s' name='serialized' direction='in'/>"
238     "          <arg type='a(s)' name='noti_arr' direction='out'/>"
239     "        </method>"
240     "        <method name='Count'>"
241     "          <arg type='s' name='appid' direction='in'/>"
242     "          <arg type='s' name='serialized' direction='in'/>"
243     "          <arg type='i' name='num' direction='out'/>"
244     "        </method>"
245     "  </interface>"
246     "  </node>";
247   GError *error = NULL;
248   GDBusNodeInfo *introspection_data =
249       g_dbus_node_info_new_for_xml(introspection_xml, &error);
250   if (!introspection_data) {
251     LOGE("g_dbus_node_info_new_for_xml is failed.");
252     if (error != NULL) {
253       LOGE("g_dbus_node_info_new_for_xml err [%s]", error->message);
254       g_error_free(error);
255     }
256     return ERROR_IO_ERROR;
257   }
258
259   registration_id_ = g_dbus_connection_register_object(
260       DBusConnectionManager::GetInst().GetConnection(),
261       path_.c_str(), introspection_data->interfaces[0],
262       &interface_vtable, this, NULL, NULL);
263   g_dbus_node_info_unref(introspection_data);
264   if (registration_id_ == 0) {
265     LOGE("register object fail");
266     return ERROR_IO_ERROR;
267   }
268
269   LOGI("RegisterGDBusInterface success");
270   return ERROR_NONE;
271 }
272
273 void DBusEventListener::Impl::UnRegisterGDBusInterface() {
274   g_dbus_connection_unregister_object(
275         DBusConnectionManager::GetInst().GetConnection(),
276         registration_id_);
277 }
278
279 bool DBusEventListener::Impl::SubscribeSignal() {
280   subscribe_id_ = g_dbus_connection_signal_subscribe(
281                     DBusConnectionManager::GetInst().GetConnection(),
282                     NULL,
283                     DBusConnectionManager::GetInst().GetInterfaceName().c_str(),
284                     NULL,
285                     path_.c_str(),
286                     NULL,
287                     G_DBUS_SIGNAL_FLAGS_NONE,
288                     SignalCb,
289                     this,
290                     NULL);
291   return subscribe_id_ > 0;
292 }
293
294 void DBusEventListener::Impl::UnSubscribeSignal() {
295   g_dbus_connection_signal_unsubscribe(
296       DBusConnectionManager::GetInst().GetConnection(), subscribe_id_);
297   subscribe_id_ = 0;
298 }
299
300 void DBusEventListener::RegisterObserver(IEventObserver* observer) {
301   if (impl_->subscribe_id_ == 0) {
302     impl_->SubscribeSignal();
303   } else {
304     impl_->UnSubscribeSignal();
305     impl_->SubscribeSignal();
306   }
307
308   if (impl_->registration_id_ == 0) {
309     impl_->RegisterGDBusInterface();
310   } else {
311     impl_->UnRegisterGDBusInterface();
312     impl_->RegisterGDBusInterface();
313   }
314   impl_->observer_ = observer;
315 }
316
317 void DBusEventListener::UnRegisterObserver(IEventObserver* observer) {
318   impl_->UnSubscribeSignal();
319   impl_->observer_ = NULL;
320 }
321
322 void DBusEventListener::NotifyObserver(
323     const IEventInfo& info, list<Bundle> serialized) {
324   impl_->observer_->OnEvent(info, serialized);
325 }
326
327 list<Bundle> DBusEventListener::NotifyObserver(const IEventInfo& info) {
328   return impl_->observer_->OnRequest(info);
329 }
330
331 int DBusEventListener::NotifyNumberRequest(const IEventInfo& info) {
332   return impl_->observer_->OnRequestNumber(info);
333 }
334
335 }  // namespace notification