tizen 2.3.1 release
[framework/web/wearable/wrt-plugins-tizen.git] / src / Push / PushManager.cpp
1 //
2 // Tizen Web Device API
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 #include <JSUtil.h>
19 #include <JSWebAPIErrorFactory.h>
20 #include <PlatformException.h>
21 #include <sstream>
22
23 #include <app_manager.h>
24
25 #include "JSPushMessage.h"
26 #include "PushMessage.h"
27 #include "PushManager.h"
28 #include "PushUtil.h"
29
30 #include <Logger.h>
31 #include <TimeTracer.h>
32 #include <GlobalContextManager.h>
33
34 #ifdef ENABLE_TIME_TRACER
35 #define _P(T, x) \
36 (TIME_TRACER_ITEM_BEGIN("PUSH_" #T "_PLATFORM", 0), x); \
37 TIME_TRACER_ITEM_END("PUSH_" #T "_PLATFORM", 0);
38 #else
39 #define _P(T, x) x
40 #endif
41
42 using namespace DeviceAPI::Common;
43
44 namespace DeviceAPI {
45 namespace Push {
46
47 static void _get_push_message(push_notification_h handle, PushMessage *message);
48 static void get_param(const char *msg, const char *name, char **value);
49
50 std::string PushManager::m_appId;
51 std::string PushManager::m_pkgId;
52 std::mutex PushManager::s_registered_mutex;
53 std::mutex PushManager::s_notification_mutex;
54
55 static void _get_push_message(push_notification_h handle, PushMessage *message)
56 {
57     LOGD("Enter");
58
59     int ret = PUSH_ERROR_NONE;
60     char *appData = NULL;
61     char *rawMessage = NULL;
62     char *alertMessage = NULL;
63     long long int date = -1;
64
65     if (!message) {
66         LOGE("message is null");
67         throw UnknownException("message is null");
68     }
69
70     ret = push_get_notification_data(handle, &appData);
71     if (ret != PUSH_ERROR_NONE) {
72         LOGE("Platform error while getting notification data: %d, %s",
73             ret, PushUtil::getPushErrorMessage(ret).c_str());
74         PushUtil::throwPushException(ret,
75             "Platform error while getting notification data");
76     }
77
78     ret = push_get_notification_message(handle, &rawMessage);
79     if (ret != PUSH_ERROR_NONE) {
80         LOGE("Platform error while getting notification message: %d, %s",
81             ret, PushUtil::getPushErrorMessage(ret).c_str());
82         PushUtil::throwPushException(ret,
83             "Platform error while getting notification message");
84     }
85
86     get_param(rawMessage, "alertMessage", &alertMessage);
87     if (!alertMessage) {
88         std::stringstream ss;
89         ss << "Platform error while getting alert message from raw message. ";
90         LOGE("%s", ss.str().c_str());
91         throw UnknownException((ss.str()).c_str());
92     }
93     free(rawMessage);
94     rawMessage = NULL;
95
96     ret = push_get_notification_time(handle, &date);
97     if (ret != PUSH_ERROR_NONE) {
98         std::stringstream ss;
99         ss << "Platform error while getting notification date/time."
100             << PushUtil::getPushErrorMessage(ret);
101         LOGE("%s", ss.str().c_str());
102     }
103
104     if (appData) {
105         message->setAppData(appData);
106     }
107
108     if (alertMessage) {
109         message->setAlertMessage(alertMessage);
110     }
111
112     if (date >= 0) {
113         message->setDate((time_t)date);
114     }
115
116     free(appData);
117     appData = NULL;
118     free(alertMessage);
119     alertMessage = NULL;
120 }
121
122 static void get_param(const char *msg, const char *name, char **value)
123 {
124     int step = 0;
125     int pos = 0;
126     int tokenpos = 0;
127
128     while (step >= 0) {
129         switch (step) {
130             case 0: // key
131                 switch (msg[pos]) {
132                     case '=':
133                         if (!strncmp(name, &msg[tokenpos], strlen(name))) {
134                             step = 2;
135                             tokenpos = pos + 1;
136                         } else {
137                             step = 1;
138                         }
139                         break;
140                     case '&':
141                         tokenpos = pos + 1;
142                         step = 0;
143                         break;
144                     case 0:
145                         step = -1;
146                         tokenpos = pos;
147                         break;
148                     default:
149                         break;
150                 }
151                 break;
152             case 1: // skip
153                 switch (msg[pos]) {
154                     case '&':
155                         tokenpos = pos + 1;
156                         step = 0;
157                         break;
158                     case 0:
159                         step = -1;
160                         tokenpos = pos;
161                         break;
162                     default:
163                         break;
164                 }
165                 break;
166             case 2: // value
167                 switch (msg[pos]) {
168                     case '&':
169                     case 0:
170                         step = -1;
171                         break;
172                     default:
173                         break;
174                 }
175                 break;
176             default:
177                 break;
178         }
179         pos++;
180     }
181
182     *value = (char*)calloc(1, pos - tokenpos);
183     if (*value != NULL) {
184         strncpy(*value, &msg[tokenpos], pos - tokenpos - 1);
185     }
186 }
187
188 static void push_connection_state_cb(push_state_e state, const char *err,
189     void *user_data)
190 {
191     LOGD("Push connection state cb with state: %d, err: %s", state, err);
192
193     PushManager *thisPushManager = static_cast<PushManager*>(user_data);
194     if (!thisPushManager) {
195         LOGE("user_data of push_connection_state_cb() is NULL.");
196     } else {
197         std::lock_guard<std::mutex> lock(PushManager::s_registered_mutex);
198         thisPushManager->m_connectionState = state;
199     }
200 }
201
202 static void push_notify_cb(push_notification_h noti, void *user_data)
203 {
204     PushMessage *pushMessage = new PushMessage();
205     PushMessage *dummy = NULL;
206
207     LOGD("Push notification cb");
208
209     try {
210         PushManager *thisPushManager = static_cast<PushManager*>(user_data);
211         if (!thisPushManager) {
212             throw UnknownException("user_data of push_notify_cb() is NULL.");
213         }
214
215         _get_push_message(noti, pushMessage);
216
217         std::lock_guard<std::mutex> lock(PushManager::s_notification_mutex);
218         std::map<JSContextRef, MultiCallbackUserDataPtr>::iterator itr =
219             thisPushManager->m_notificationCallback.begin();
220         while (itr != thisPushManager->m_notificationCallback.end()) {
221             MultiCallbackUserDataPtr callback = itr->second;
222             if (callback) {
223                 JSContextRef context = callback->getContext();
224                 if(GlobalContextManager::getInstance()->isAliveGlobalContext(context)) {
225
226                     dummy = new PushMessage(pushMessage->getAppData(),
227                             pushMessage->getAlertMessage(),
228                             pushMessage->getDate());
229
230                     JSObjectRef pushMessageObj =
231                         JSObjectMake(context, JSPushMessage::getClassRef(), NULL);
232                     JSPushMessage::setPrivateObject(context, pushMessageObj, dummy);
233
234                     callback->invokeCallback("onsuccess", pushMessageObj);
235                 }
236             }
237             ++itr;
238         }
239     } catch (const BasePlatformException &err) {
240         LOGE("%s", err.getMessage().c_str());
241     }
242
243     delete pushMessage;
244     pushMessage = NULL;
245 }
246
247 static void push_registration_result_cb(push_result_e result, const char *msg,
248     void *user_data)
249 {
250     LOGD("Push registration cb");
251     int ret;
252     char *tmp = NULL;
253     PushMultiCallbackUserDataHolder* holder = NULL;
254
255     try {
256         holder = static_cast<PushMultiCallbackUserDataHolder*>(user_data);
257         if (!holder) {
258             LOGE("callback holder is null");
259             return;
260         }
261
262         MultiCallbackUserDataPtr callback = holder->ptr;
263         if (!callback) {
264             delete holder;
265             holder = NULL;
266             return;
267         }
268
269         JSContextRef context = callback->getContext();
270         if( !GlobalContextManager::getInstance()->isAliveGlobalContext(context)){
271             LOGE("context was closed");
272             delete holder;
273             holder = NULL;
274             return;
275         }
276
277         if (result == PUSH_RESULT_SUCCESS) {
278             ret = push_get_registration_id(PushManager::getInstance()->m_connectionHandle, &tmp);
279             if (PUSH_ERROR_NONE != ret) {
280                 delete holder;
281                 holder = NULL;
282
283                 LOGE("Platform error while getting registration id: %d, %s", ret,
284                     PushUtil::getPushErrorMessage(ret).c_str());
285                 PushUtil::throwPushException(ret,
286                     "Platform error while getting registration id");
287             }
288             if (tmp) {
289                 std::string registrationId(tmp);
290                 callback->invokeCallback("onsuccess",
291                     JSUtil::toJSValueRef(context, registrationId));
292                 free(tmp);
293             }
294         } else {
295             UnknownException error(msg == NULL ? "Unknown" : msg);
296             JSObjectRef errorObj =
297                 JSWebAPIErrorFactory::makeErrorObject(context, error);
298             callback->invokeCallback("onerror", errorObj);
299         }
300     } catch (const BasePlatformException &err) {
301         LOGE("%s", err.getMessage().c_str());
302     }
303
304     delete holder;
305     holder = NULL;
306 }
307
308 static void push_unregistration_result_cb(push_result_e result, const char *msg,
309     void *user_data)
310 {
311     LOGD("Push unregistration cb");
312     PushMultiCallbackUserDataHolder* holder = NULL;
313
314     try {
315         holder = static_cast<PushMultiCallbackUserDataHolder*>(user_data);
316         if (!holder) {
317             LOGE("callback holder is null");
318             return;
319         }
320
321         MultiCallbackUserDataPtr callback = holder->ptr;
322         if (!callback) {
323             delete holder;
324             holder = NULL;
325             return;
326         }
327
328         JSContextRef context = callback->getContext();
329         if( !GlobalContextManager::getInstance()->isAliveGlobalContext(context)){
330             LOGE("context was closed");
331             delete holder;
332             holder = NULL;
333             return;
334         }
335
336         if (result == PUSH_RESULT_SUCCESS) {
337             callback->invokeCallback("onsuccess");
338         } else {
339             LOGD("Enter msg: %s", msg);
340             UnknownException error(msg == NULL ? "Unknown" : msg);
341             JSObjectRef errorObj =
342                 JSWebAPIErrorFactory::makeErrorObject(context, error);
343             callback->invokeCallback("onerror", errorObj);
344         }
345     } catch (const BasePlatformException &err) {
346         LOGE("%s", err.getMessage().c_str());
347     }
348
349     delete holder;
350     holder = NULL;
351 }
352
353 static gboolean push_unregistration_result_cb1(void *user_data)
354 {
355     LOGD("Push unregistration cb1");
356     PushMultiCallbackUserDataHolder* holder = NULL;
357
358     try {
359         holder = static_cast<PushMultiCallbackUserDataHolder*>(user_data);
360         if (!holder) {
361             LOGE("callback holder is null");
362             return false;
363         }
364
365         MultiCallbackUserDataPtr callback = holder->ptr;
366         if (!callback) {
367             delete holder;
368             holder = NULL;
369             return false;
370         }
371
372         callback->invokeCallback("onsuccess");
373     } catch (const BasePlatformException &err) {
374         LOGE("%s", err.getMessage().c_str());
375     }
376
377     delete holder;
378     holder = NULL;
379     return false;
380 }
381
382 PushManager::PushManager():
383     m_connectionState(PUSH_STATE_UNREGISTERED),
384     m_connectionHandle(NULL)
385 {
386     LOGD("Connecting to the push service...");
387     setAppId();
388
389     int ret = push_connect(m_pkgId.c_str(), push_connection_state_cb,
390         push_notify_cb, this, &m_connectionHandle);
391
392     if (PUSH_ERROR_NONE != ret) {
393         LOGE("Error while connecting to the push service: %d, %s", ret,
394             PushUtil::getPushErrorMessage(ret).c_str());
395     }
396 }
397
398 PushManager::~PushManager()
399 {
400     LOGD("Disconnecting to the push service...");
401
402     push_disconnect(m_connectionHandle);
403 }
404
405 PushManager *PushManager::getInstance() {
406     LOGD("Getting instance of PushManager...");
407     static PushManager instance;
408     return &instance;
409 }
410
411 void PushManager::setAppId() {
412     app_info_h handle;
413     char *_pkg_id = NULL;
414     char *app_id = NULL;
415     int pid = getpid();
416     int ret = app_manager_get_app_id(pid, &app_id);
417     if (ret != APP_MANAGER_ERROR_NONE) {
418         LOGE("Fail to get appid: %d", ret);
419         return;
420     }
421     m_appId = app_id;
422     free(app_id);
423
424     ret = app_manager_get_app_info(m_appId.c_str(), &handle);
425     if (ret != APP_MANAGER_ERROR_NONE) {
426         LOGE("WrtAccess initialization failed: %d", ret);
427         return;
428     }
429
430     ret = app_info_get_package(handle, &_pkg_id);
431     if ((ret != APP_MANAGER_ERROR_NONE) || (_pkg_id == NULL)) {
432         LOGW("Fail to get pkg id: %d", ret);
433     }
434
435     ret = app_info_destroy(handle);
436     if (ret != APP_MANAGER_ERROR_NONE) {
437         LOGE("WrtAccess initialization failed: %d", ret);
438         return;
439     }
440
441     if (_pkg_id) {
442         m_pkgId = _pkg_id;
443         free(_pkg_id);
444     }
445 }
446
447 void PushManager::registerService(ApplicationControlPtr appControl,
448         MultiCallbackUserDataPtr callback, JSContextRef context)
449 {
450     LOGD("Entered");
451     int ret;
452     app_control_h service;
453
454     ret = _P(registerService, app_control_create(&service));
455     if (ret != APP_CONTROL_ERROR_NONE) {
456         LOGE("Platform error while creating service: %d, %s", ret,
457             PushUtil::getPushErrorMessage(ret).c_str());
458         PushUtil::throwPushException(ret, "Platform error while create service");
459     }
460
461     if (appControl->getOperation().compare("") != 0) {
462         ret = _P(registerService, app_control_set_operation(service,
463             appControl->getOperation().c_str()));
464         if (ret != APP_CONTROL_ERROR_NONE) {
465             LOGE("Platform error while setting operation to appcontrol: %d, %s",
466                 PushUtil::getPushErrorMessage(ret).c_str());
467             PushUtil::throwPushException(ret,
468                 "Platform error while setting operation to appcontrol");
469         }
470     } else {
471         throw InvalidValuesException(
472             "the operation of application control is invalid.");
473     }
474
475     if (appControl->getUri().compare("") != 0) {
476         ret = _P(registerService, app_control_set_uri(service,
477             appControl->getUri().c_str()));
478         if (ret != APP_CONTROL_ERROR_NONE) {
479             LOGW("Platform error while setting uri to appControl: %d, %s",
480                 ret, PushUtil::getPushErrorMessage(ret).c_str());
481         }
482     }
483
484     if (appControl->getMime().compare("") != 0) {
485         ret = _P(registerService, app_control_set_mime(service,
486             appControl->getMime().c_str()));
487         if (ret != APP_CONTROL_ERROR_NONE) {
488             LOGW("Platform error while setting mime to appControl: %d, %s",
489                 ret, PushUtil::getPushErrorMessage(ret).c_str());
490         }
491     }
492
493     ret = _P(registerService, app_control_set_app_id(service, m_appId.c_str()));
494     if (ret != APP_CONTROL_ERROR_NONE) {
495         LOGE("Platform error while setting appId to appControl: %d, %s",
496             ret, PushUtil::getPushErrorMessage(ret).c_str());
497         PushUtil::throwPushException(ret,
498             "Platform error while setting appId to appControl");
499     }
500
501     std::vector<ApplicationControlDataPtr> controlDataArray =
502         appControl->getAppControlDataArray();
503     if (!controlDataArray.empty()) {
504         std::string key;
505         const char **arr = NULL;
506
507         for (size_t i = 0; i < controlDataArray.size(); i++) {
508             key = controlDataArray.at(i)->getKey();
509             if (key.empty()) {
510                 LOGW("Invalid key for %d in ApplicationControl's data array.", i);
511                 continue;
512             }
513
514             std::vector<std::string> valueArray =
515                 controlDataArray.at(i)->getValue();
516             size_t size = valueArray.size();
517
518             arr = (const char**)calloc(sizeof(char*), size);
519             if (arr == NULL) {
520                 LOGE("calloc failed");
521                 throw UnknownException(
522                     "Out of Memory: Can't allocate value array for ApplicationControl's data");
523             }
524
525             for (size_t j = 0; j < size; j++) {
526                 arr[j] = valueArray.at(j).c_str();
527             }
528
529             if (size == 1) {
530                 ret = _P(registerService, app_control_add_extra_data(service,
531                     (const char*)key.c_str(), arr[0]));
532                 if (ret != APP_CONTROL_ERROR_NONE) {
533                     LOGW("Platform error while adding extra data to appControl: %d, %s",
534                         ret, PushUtil::getPushErrorMessage(ret).c_str());
535                 }
536             } else {
537                 ret = _P(registerService, app_control_add_extra_data_array(
538                     service, (const char*)key.c_str(), arr, size));
539                 if (ret != APP_CONTROL_ERROR_NONE) {
540                     LOGW("Platform error while adding extra data array to appControl: %d, %s",
541                         ret, PushUtil::getPushErrorMessage(ret).c_str());
542                 }
543             }
544
545             if (arr) {
546                 free(arr);
547             }
548         }
549     }
550
551     std::lock_guard<std::mutex> lock(s_registered_mutex);
552     m_registeredCallback[context] = callback;
553
554     PushMultiCallbackUserDataHolder* holder =
555         new(std::nothrow) PushMultiCallbackUserDataHolder();
556     if (!holder) {
557         LOGE("Failed to allocate memory");
558
559         ret = app_control_destroy(service);
560         if (ret != APP_CONTROL_ERROR_NONE) {
561             LOGE("Failed to destroy app control: %d, %s", ret,
562                 PushUtil::getPushErrorMessage(ret).c_str());
563         }
564         throw UnknownException("Failed to allocate memory");
565     }
566     holder->ptr = callback;
567
568     ret = _P(registerService, push_register(m_connectionHandle, service,
569                 push_registration_result_cb, (void*)holder));
570     if (ret != PUSH_ERROR_NONE) {
571         delete holder;
572         holder = NULL;
573
574         int r = app_control_destroy(service);
575         if (r != APP_CONTROL_ERROR_NONE) {
576             LOGE("Failed to destroy app control: %d, %s", ret,
577                 PushUtil::getPushErrorMessage(ret).c_str());
578         }
579         LOGE("Platform error while registering the application: %d, %s", ret,
580             PushUtil::getPushErrorMessage(ret).c_str());
581         PushUtil::throwPushException(ret,
582             "Platform error while registering the application.");
583     }
584
585     _P(registerService, app_control_destroy(service));
586 }
587
588 void PushManager::unregisterService(MultiCallbackUserDataPtr callback, JSContextRef context,
589         bool is_callback_obj)
590 {
591     LOGD("Entered");
592     int ret;
593
594     std::lock_guard<std::mutex> lock(s_registered_mutex);
595     std::map<JSContextRef, MultiCallbackUserDataPtr>::iterator itr =
596         m_registeredCallback.find(context);
597     if (itr != m_registeredCallback.end()) {
598         LOGD("context will be removed");
599         m_registeredCallback.erase(context);
600     }
601
602     if (!is_callback_obj) {
603         return;
604     }
605
606     PushMultiCallbackUserDataHolder* holder =
607         new(std::nothrow) PushMultiCallbackUserDataHolder();
608     if (!holder) {
609         LOGE("Failed to allocate memory");
610         throw UnknownException("Failed to allocate memory");
611     }
612     holder->ptr = callback;
613
614     if (!m_registeredCallback.empty() || m_connectionState == PUSH_STATE_UNREGISTERED) {
615         if (!g_idle_add(push_unregistration_result_cb1, (void*)holder)) {
616             LOGE("g_idle_add failed");
617             delete holder;
618             holder = NULL;
619             return;
620         }
621     } else {
622         ret = _P(unregisterService, push_deregister(m_connectionHandle,
623                     push_unregistration_result_cb, (void*)holder));
624         if (ret != PUSH_ERROR_NONE) {
625             LOGD("push_deregister failed: %d", ret);
626             delete holder;
627             holder = NULL;
628             PushUtil::throwPushException(ret,
629                     "Platform error while unregistering the application");
630         }
631     }
632 }
633
634 void PushManager::connectService(MultiCallbackUserDataPtr notificationCallback,
635         JSContextRef context)
636 {
637     LOGD("Entered");
638     if (notificationCallback) {
639         std::lock_guard<std::mutex> lock(s_notification_mutex);
640         m_notificationCallback[context] = notificationCallback;
641     } else {
642         LOGE("notificationCallback of arguments is NULL.");
643         throw UnknownException("notificationCallback of arguments is NULL.");
644     }
645 }
646
647 void PushManager::disconnectService(JSContextRef context)
648 {
649     LOGD("Entered");
650     std::lock_guard<std::mutex> lock(s_notification_mutex);
651     std::map<JSContextRef, MultiCallbackUserDataPtr>::iterator itr =
652         m_notificationCallback.find(context);
653     if (itr != m_registeredCallback.end()) {
654         LOGD("context will be removed");
655         m_notificationCallback.erase(context);
656     }
657 }
658
659 std::string PushManager::getRegistrationId(JSContextRef context)
660 {
661     LOGD("Entered");
662     int ret;
663     char *regId = NULL;
664     std::string str = "";
665
666     s_registered_mutex.lock();
667     std::map<JSContextRef, MultiCallbackUserDataPtr>::iterator itr =
668         m_registeredCallback.find(context);
669     if (itr == m_registeredCallback.end()) {
670         s_registered_mutex.unlock();
671         return str;
672     }
673     s_registered_mutex.unlock();
674
675     ret = _P(getRegistrationId, push_get_registration_id(m_connectionHandle, &regId));
676     if (ret != PUSH_ERROR_NONE) {
677         LOGE("Platform error while getting registration id: %d, %s", ret,
678             PushUtil::getPushErrorMessage(ret).c_str());
679     } else {
680         if (regId) {
681             str = regId;
682             free(regId);
683         }
684     }
685
686     return str;
687 }
688
689 void PushManager::getUnreadNotifications()
690 {
691     LOGD("Enter");
692
693     int ret = push_request_unread_notification(m_connectionHandle);
694     if (ret != PUSH_ERROR_NONE) {
695         LOGE("Failed to request unread push messages: %d, %s", ret,
696                 PushUtil::getPushErrorMessage(ret).c_str());
697         PushUtil::throwPushException(ret,
698                 "Failed to request unread push messages");
699     }
700 }
701
702 } // Push
703 } // DeviceAPI