2 // Tizen Web Device API
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
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
9 // http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <JSWebAPIErrorFactory.h>
20 #include <PlatformException.h>
23 #include <app_manager.h>
25 #include "JSPushMessage.h"
26 #include "PushMessage.h"
27 #include "PushManager.h"
31 #include <TimeTracer.h>
32 #include <GlobalContextManager.h>
34 #ifdef ENABLE_TIME_TRACER
36 (TIME_TRACER_ITEM_BEGIN("PUSH_" #T "_PLATFORM", 0), x); \
37 TIME_TRACER_ITEM_END("PUSH_" #T "_PLATFORM", 0);
42 using namespace DeviceAPI::Common;
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);
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;
55 static void _get_push_message(push_notification_h handle, PushMessage *message)
59 int ret = PUSH_ERROR_NONE;
61 char *rawMessage = NULL;
62 char *alertMessage = NULL;
63 long long int date = -1;
66 LOGE("message is null");
67 throw UnknownException("message is null");
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");
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");
86 get_param(rawMessage, "alertMessage", &alertMessage);
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());
96 ret = push_get_notification_time(handle, &date);
97 if (ret != PUSH_ERROR_NONE) {
99 ss << "Platform error while getting notification date/time."
100 << PushUtil::getPushErrorMessage(ret);
101 LOGE("%s", ss.str().c_str());
105 message->setAppData(appData);
109 message->setAlertMessage(alertMessage);
113 message->setDate((time_t)date);
122 static void get_param(const char *msg, const char *name, char **value)
133 if (!strncmp(name, &msg[tokenpos], strlen(name))) {
182 *value = (char*)calloc(1, pos - tokenpos);
183 if (*value != NULL) {
184 strncpy(*value, &msg[tokenpos], pos - tokenpos - 1);
188 static void push_connection_state_cb(push_state_e state, const char *err,
191 LOGD("Push connection state cb with state: %d, err: %s", state, err);
193 PushManager *thisPushManager = static_cast<PushManager*>(user_data);
194 if (!thisPushManager) {
195 LOGE("user_data of push_connection_state_cb() is NULL.");
197 std::lock_guard<std::mutex> lock(PushManager::s_registered_mutex);
198 thisPushManager->m_connectionState = state;
202 static void push_notify_cb(push_notification_h noti, void *user_data)
204 PushMessage *pushMessage = new PushMessage();
205 PushMessage *dummy = NULL;
207 LOGD("Push notification cb");
210 PushManager *thisPushManager = static_cast<PushManager*>(user_data);
211 if (!thisPushManager) {
212 throw UnknownException("user_data of push_notify_cb() is NULL.");
215 _get_push_message(noti, pushMessage);
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;
223 JSContextRef context = callback->getContext();
224 if(GlobalContextManager::getInstance()->isAliveGlobalContext(context)) {
226 dummy = new PushMessage(pushMessage->getAppData(),
227 pushMessage->getAlertMessage(),
228 pushMessage->getDate());
230 JSObjectRef pushMessageObj =
231 JSObjectMake(context, JSPushMessage::getClassRef(), NULL);
232 JSPushMessage::setPrivateObject(context, pushMessageObj, dummy);
234 callback->invokeCallback("onsuccess", pushMessageObj);
239 } catch (const BasePlatformException &err) {
240 LOGE("%s", err.getMessage().c_str());
247 static void push_registration_result_cb(push_result_e result, const char *msg,
250 LOGD("Push registration cb");
253 PushMultiCallbackUserDataHolder* holder = NULL;
256 holder = static_cast<PushMultiCallbackUserDataHolder*>(user_data);
258 LOGE("callback holder is null");
262 MultiCallbackUserDataPtr callback = holder->ptr;
269 JSContextRef context = callback->getContext();
270 if( !GlobalContextManager::getInstance()->isAliveGlobalContext(context)){
271 LOGE("context was closed");
277 if (result == PUSH_RESULT_SUCCESS) {
278 ret = push_get_registration_id(PushManager::getInstance()->m_connectionHandle, &tmp);
279 if (PUSH_ERROR_NONE != ret) {
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");
289 std::string registrationId(tmp);
290 callback->invokeCallback("onsuccess",
291 JSUtil::toJSValueRef(context, registrationId));
295 UnknownException error(msg == NULL ? "Unknown" : msg);
296 JSObjectRef errorObj =
297 JSWebAPIErrorFactory::makeErrorObject(context, error);
298 callback->invokeCallback("onerror", errorObj);
300 } catch (const BasePlatformException &err) {
301 LOGE("%s", err.getMessage().c_str());
308 static void push_unregistration_result_cb(push_result_e result, const char *msg,
311 LOGD("Push unregistration cb");
312 PushMultiCallbackUserDataHolder* holder = NULL;
315 holder = static_cast<PushMultiCallbackUserDataHolder*>(user_data);
317 LOGE("callback holder is null");
321 MultiCallbackUserDataPtr callback = holder->ptr;
328 JSContextRef context = callback->getContext();
329 if( !GlobalContextManager::getInstance()->isAliveGlobalContext(context)){
330 LOGE("context was closed");
336 if (result == PUSH_RESULT_SUCCESS) {
337 callback->invokeCallback("onsuccess");
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);
345 } catch (const BasePlatformException &err) {
346 LOGE("%s", err.getMessage().c_str());
353 static gboolean push_unregistration_result_cb1(void *user_data)
355 LOGD("Push unregistration cb1");
356 PushMultiCallbackUserDataHolder* holder = NULL;
359 holder = static_cast<PushMultiCallbackUserDataHolder*>(user_data);
361 LOGE("callback holder is null");
365 MultiCallbackUserDataPtr callback = holder->ptr;
372 callback->invokeCallback("onsuccess");
373 } catch (const BasePlatformException &err) {
374 LOGE("%s", err.getMessage().c_str());
382 PushManager::PushManager():
383 m_connectionState(PUSH_STATE_UNREGISTERED),
384 m_connectionHandle(NULL)
386 LOGD("Connecting to the push service...");
389 int ret = push_connect(m_pkgId.c_str(), push_connection_state_cb,
390 push_notify_cb, this, &m_connectionHandle);
392 if (PUSH_ERROR_NONE != ret) {
393 LOGE("Error while connecting to the push service: %d, %s", ret,
394 PushUtil::getPushErrorMessage(ret).c_str());
398 PushManager::~PushManager()
400 LOGD("Disconnecting to the push service...");
402 push_disconnect(m_connectionHandle);
405 PushManager *PushManager::getInstance() {
406 LOGD("Getting instance of PushManager...");
407 static PushManager instance;
411 void PushManager::setAppId() {
413 char *_pkg_id = NULL;
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);
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);
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);
435 ret = app_info_destroy(handle);
436 if (ret != APP_MANAGER_ERROR_NONE) {
437 LOGE("WrtAccess initialization failed: %d", ret);
447 void PushManager::registerService(ApplicationControlPtr appControl,
448 MultiCallbackUserDataPtr callback, JSContextRef context)
452 app_control_h service;
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");
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");
471 throw InvalidValuesException(
472 "the operation of application control is invalid.");
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());
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());
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");
501 std::vector<ApplicationControlDataPtr> controlDataArray =
502 appControl->getAppControlDataArray();
503 if (!controlDataArray.empty()) {
505 const char **arr = NULL;
507 for (size_t i = 0; i < controlDataArray.size(); i++) {
508 key = controlDataArray.at(i)->getKey();
510 LOGW("Invalid key for %d in ApplicationControl's data array.", i);
514 std::vector<std::string> valueArray =
515 controlDataArray.at(i)->getValue();
516 size_t size = valueArray.size();
518 arr = (const char**)calloc(sizeof(char*), size);
520 LOGE("calloc failed");
521 throw UnknownException(
522 "Out of Memory: Can't allocate value array for ApplicationControl's data");
525 for (size_t j = 0; j < size; j++) {
526 arr[j] = valueArray.at(j).c_str();
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());
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());
551 std::lock_guard<std::mutex> lock(s_registered_mutex);
552 m_registeredCallback[context] = callback;
554 PushMultiCallbackUserDataHolder* holder =
555 new(std::nothrow) PushMultiCallbackUserDataHolder();
557 LOGE("Failed to allocate memory");
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());
564 throw UnknownException("Failed to allocate memory");
566 holder->ptr = callback;
568 ret = _P(registerService, push_register(m_connectionHandle, service,
569 push_registration_result_cb, (void*)holder));
570 if (ret != PUSH_ERROR_NONE) {
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());
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.");
585 _P(registerService, app_control_destroy(service));
588 void PushManager::unregisterService(MultiCallbackUserDataPtr callback, JSContextRef context,
589 bool is_callback_obj)
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);
602 if (!is_callback_obj) {
606 PushMultiCallbackUserDataHolder* holder =
607 new(std::nothrow) PushMultiCallbackUserDataHolder();
609 LOGE("Failed to allocate memory");
610 throw UnknownException("Failed to allocate memory");
612 holder->ptr = callback;
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");
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);
628 PushUtil::throwPushException(ret,
629 "Platform error while unregistering the application");
634 void PushManager::connectService(MultiCallbackUserDataPtr notificationCallback,
635 JSContextRef context)
638 if (notificationCallback) {
639 std::lock_guard<std::mutex> lock(s_notification_mutex);
640 m_notificationCallback[context] = notificationCallback;
642 LOGE("notificationCallback of arguments is NULL.");
643 throw UnknownException("notificationCallback of arguments is NULL.");
647 void PushManager::disconnectService(JSContextRef context)
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);
659 std::string PushManager::getRegistrationId(JSContextRef context)
664 std::string str = "";
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();
673 s_registered_mutex.unlock();
675 ret = _P(getRegistrationId, push_get_registration_id(m_connectionHandle, ®Id));
676 if (ret != PUSH_ERROR_NONE) {
677 LOGE("Platform error while getting registration id: %d, %s", ret,
678 PushUtil::getPushErrorMessage(ret).c_str());
689 void PushManager::getUnreadNotifications()
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");