Commit f82c47cb authored by DanmeiChen's avatar DanmeiChen

add ios push notification in lib

parent 2df9c680
......@@ -6204,7 +6204,9 @@ void sip_config_uninit(LinphoneCore *lc)
for(elem=config->proxies;elem!=NULL;elem=bctbx_list_next(elem)){
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)(elem->data);
_linphone_proxy_config_unpublish(cfg); /* to unpublish without changing the stored flag enable_publish */
_linphone_proxy_config_unregister(cfg); /* to unregister without changing the stored flag enable_register */
if (!linphone_proxy_config_is_push_notification_allowed(cfg)) {
_linphone_proxy_config_unregister(cfg); /* to unregister without changing the stored flag enable_register */
}
}
ms_message("Unregistration started.");
......@@ -6215,7 +6217,9 @@ void sip_config_uninit(LinphoneCore *lc)
for(elem=config->proxies;elem!=NULL;elem=bctbx_list_next(elem)){
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)(elem->data);
LinphoneRegistrationState state = linphone_proxy_config_get_state(cfg);
still_registered = (state==LinphoneRegistrationOk||state==LinphoneRegistrationProgress);
if (!linphone_proxy_config_is_push_notification_allowed(cfg)) {
still_registered = (state==LinphoneRegistrationOk||state==LinphoneRegistrationProgress);
}
}
ms_usleep(100000);
}
......
......@@ -6021,6 +6021,69 @@ LINPHONE_PUBLIC void linphone_core_load_config_from_xml(LinphoneCore *lc, const
**/
LINPHONE_PUBLIC void linphone_core_ensure_registered(LinphoneCore *lc);
/**
* Get the currently set push notification type if any
* @param[in] lc The #LinphoneCore
* @return the push notification type
* @ingroup misc
**/
LINPHONE_PUBLIC const char* linphone_core_get_push_notification_type(LinphoneCore *lc);
/**
* Get the currently set push notification application id if any
* @param[in] lc The #LinphoneCore
* @return the push notification application id
* @ingroup misc
**/
LINPHONE_PUBLIC const char* linphone_core_get_push_notification_application_id(LinphoneCore *lc);
/**
* Get the currently set push notification token if any
* @param[in] lc The #LinphoneCore
* @return the push notification token
* @ingroup misc
**/
LINPHONE_PUBLIC const char* linphone_core_get_push_notification_token(LinphoneCore *lc);
/**
* @brief Tells whether the push notification timeout is needed.
* @param[in] lc The #LinphoneCore
* @return A boolean telling whether the push notification timeout is needed or not
* @ingroup misc
**/
LINPHONE_PUBLIC bool_t linphone_core_is_push_notification_timeout_needed(LinphoneCore *lc);
/**
* Set the push notification token returned by Firebase or Pushkit.
* @param[in] lc The #LinphoneCore
* @param[in] token The push notification token
* @ingroup misc
**/
LINPHONE_PUBLIC void linphone_core_set_push_notification_token(LinphoneCore *lc, const char* token);
/**
* Tells to LinphoneCore to use push notification timeout
* @param[in] lc LinphoneCore object
* @param[in] enable A boolean value telling whether the push notification timeout is needed or not
* @ingroup misc
*/
LINPHONE_PUBLIC void linphone_core_set_push_notification_timeout_needed(LinphoneCore *lc, bool_t timeoutNeeded);
/**
* Set the push notification type.
* @param[in] lc The #LinphoneCore
* @param[in] token The push notification type
* @ingroup misc
**/
LINPHONE_PUBLIC void linphone_core_set_push_notification_type(LinphoneCore *lc, const char* type);
/**
* Set the push notification application id.
* @param[in] lc The #LinphoneCore
* @param[in] token The push notification application id
* @ingroup misc
**/
LINPHONE_PUBLIC void linphone_core_set_push_notification_application_id(LinphoneCore *lc, const char* appId);
#ifdef __cplusplus
}
......
......@@ -31,6 +31,9 @@ set(LIBS
)
if(APPLE)
list(APPEND LIBS "-framework CFNetwork")
list(APPEND LIBS "-framework PushKit")
list(APPEND LIBS "-framework UserNotifications")
list(APPEND LIBS "-framework UIKit")
endif()
if(WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
list(APPEND LIBS "Ws2_32")
......@@ -380,7 +383,9 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES
set(LINPHONE_OBJC_SOURCE_FILES)
if (APPLE)
list(APPEND LINPHONE_OBJC_SOURCE_FILES core/paths/paths-apple.mm)
list(APPEND LINPHONE_OBJC_SOURCE_FILES core/PushRegistry.mm)
list(APPEND LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES core/paths/paths-apple.h)
list(APPEND LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES core/PushRegistry.h)
list(APPEND LINPHONE_OBJC_SOURCE_FILES core/platform-helpers/ios-platform-helpers.mm)
elseif (ANDROID)
list(APPEND LINPHONE_CXX_OBJECTS_SOURCE_FILES core/paths/paths-android.cpp core/platform-helpers/android-platform-helpers.cpp)
......
......@@ -130,4 +130,36 @@ bool_t linphone_core_is_friend_list_subscription_enabled(LinphoneCore *lc) {
void linphone_core_ensure_registered(LinphoneCore *lc) {
L_GET_CPP_PTR_FROM_C_OBJECT(lc)->pushNotificationReceived();
}
\ No newline at end of file
}
const char* linphone_core_get_push_notification_type(LinphoneCore *lc) {
return L_STRING_TO_C(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->getPushNotificationType());
}
const char* linphone_core_get_push_notification_application_id(LinphoneCore *lc) {
return L_STRING_TO_C(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->getPushNotificationappId());
}
const char* linphone_core_get_push_notification_token(LinphoneCore *lc) {
return L_STRING_TO_C(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->getPushNotificationToken());
}
bool_t linphone_core_is_push_notification_timeout_needed(LinphoneCore *lc) {
return L_GET_CPP_PTR_FROM_C_OBJECT(lc)->isPushNotificationTimeoutNeeded() ? TRUE : FALSE;
}
void linphone_core_set_push_notification_token(LinphoneCore *lc, const char* token) {
L_GET_CPP_PTR_FROM_C_OBJECT(lc)->setPushNotificationToken(L_C_TO_STRING(token));
}
void linphone_core_set_push_notification_timeout_needed(LinphoneCore *lc, bool_t timeoutNeeded) {
L_GET_CPP_PTR_FROM_C_OBJECT(lc)->setPushNotificationTimeoutNeeded(timeoutNeeded ? true : false);
}
void linphone_core_set_push_notification_type(LinphoneCore *lc, const char* type) {
L_GET_CPP_PTR_FROM_C_OBJECT(lc)->setPushNotificationType(L_C_TO_STRING(type));
}
void linphone_core_set_push_notification_application_id(LinphoneCore *lc, const char* appId) {
L_GET_CPP_PTR_FROM_C_OBJECT(lc)->setPushNotificationappId(L_C_TO_STRING(appId));
}
#include <PushKit/PushKit.h>
#include <PushKit/PKPushRegistry.h>
#include <UserNotifications/UserNotifications.h>
#include <UIKit/UIKit.h>
// TODO: Remove me
#include "private.h"
@interface RegistryDelegate : NSObject <PKPushRegistryDelegate> {
std::shared_ptr<LinphonePrivate::Core> pcore;
}
- (void)setCore:(std::shared_ptr<LinphonePrivate::Core> )core;
@end
/*
linphone
Copyright (C) 2017 Belledonne Communications SARL
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "PushRegistry.h"
// TODO: Remove me
#include "private.h"
@implementation RegistryDelegate
- (void)setCore:(std::shared_ptr<LinphonePrivate::Core> )core {
pcore = core;
}
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {
NSData *tokenData = credentials.token;
if (tokenData != nil) {
const unsigned char *tokenBuffer = (const unsigned char *)[tokenData bytes];
NSMutableString *tokenString = [NSMutableString stringWithCapacity:[tokenData length] * 2];
for (int i = 0; i < int([tokenData length]); ++i) {
[tokenString appendFormat:@"%02X", (unsigned int)tokenBuffer[i]];
}
dispatch_async(dispatch_get_main_queue(), ^{
linphone_core_set_push_notification_token(pcore->getCCore(), [tokenString UTF8String]);
});
}
}
- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(NSString *)type {
ms_message("[PushKit] Token invalidated");
dispatch_async(dispatch_get_main_queue(), ^{linphone_core_set_push_notification_token(pcore->getCCore(),NULL);});
}
- (void)processPush:(NSDictionary *)userInfo {
ms_message("[PushKit] Notification [%p] received with pay load : %s", userInfo, userInfo.description.UTF8String);
//to avoid IOS to suspend the app before being able to launch long running task
[self processRemoteNotification:userInfo];
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
[self processPush:payload.dictionaryPayload];
dispatch_async(dispatch_get_main_queue(), ^{completion();});
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
[self processPush:payload.dictionaryPayload];
}
- (void)processRemoteNotification:(NSDictionary *)userInfo {
if (linphone_core_get_calls(pcore->getCCore())) {
// if there are calls, obviously our TCP socket shall be working
ms_warning("Notification [%p] has no need to be processed because there already is an active call.", userInfo);
return;
}
NSDictionary *aps = [userInfo objectForKey:@"aps"];
if (!aps) {
ms_error("Notification [%p] was empy, it's impossible to process it.", userInfo);
return;
}
NSString *loc_key = [aps objectForKey:@"loc-key"] ?: [[aps objectForKey:@"alert"] objectForKey:@"loc-key"];
if (!loc_key) {
ms_error("Notification [%p] has no loc_key, it's impossible to process it.", userInfo);
return;
}
NSString *uuid = [NSString stringWithFormat:@"<urn:uuid:%@>", [NSString stringWithUTF8String:lp_config_get_string(pcore->getCCore()->config,"misc","uuid",NULL)]];
NSString *sipInstance = [aps objectForKey:@"uuid"];
if (sipInstance && uuid && ![sipInstance isEqualToString:uuid]) {
ms_error("Notification [%p] was intended for another device, ignoring it.", userInfo);
ms_error("My sip instance is: [%s], push was intended for: [%s].", uuid, [sipInstance UTF8String]);
return;
}
LinphonePrivate::PlatformHelpers *iosHelper = getPlatformHelpers(pcore->getCCore());
NSString *callId = [aps objectForKey:@"call-id"] ?: @"";
if (callId && ([UIApplication sharedApplication].applicationState != UIApplicationStateActive))// && [self addLongTaskIDforCallID:callId])
iosHelper->startPushLongRunningTask(loc_key.UTF8String, callId.UTF8String);
// if we receive a push notification, it is probably because our TCP background socket was no more working.
// As a result, break it and refresh registers in order to make sure to receive incoming INVITE or MESSAGE
if (!linphone_core_is_network_reachable(pcore->getCCore())) {
ms_message("Notification [%p] network is down, restarting it.", userInfo);
}
if ([callId isEqualToString:@""]) {
// Present apn pusher notifications for info
ms_message("Notification [%p] came from flexisip-pusher.", userInfo);
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) {
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = @"APN Pusher";
content.body = @"Push notification received !";
UNNotificationRequest *req = [UNNotificationRequest requestWithIdentifier:@"call_request" content:content trigger:NULL];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:req withCompletionHandler:^(NSError * _Nullable error) {
// Enable or disable features based on authorization.
if (error) {
ms_message("Error while adding notification request :");
ms_message("%s", error.description.UTF8String);
}
}];
} else {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.repeatInterval = 0;
notification.alertBody = @"Push notification received !";
notification.alertTitle = @"APN Pusher";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
} else{
//[LinphoneManager.instance addPushCallId:callId];
}
ms_message("Notification [%p] processed", userInfo);
}
@end
......@@ -125,6 +125,11 @@ private:
bool isInBackground = false;
bool isFriendListSubscriptionEnabled = false;
std::string pushNotificationType;
std::string pushNotificationAppId;
std::string pushNotificationToken;
bool pushNotificationTimeoutNeeded = true;
std::list<CoreListener *> listeners;
std::list<std::shared_ptr<Call>> calls;
......
......@@ -512,5 +512,85 @@ void Core::doLater(const std::function<void ()> &something){
getPrivate()->doLater(something);
}
const string& Core::getPushNotificationType() const {
L_D();
return d->pushNotificationType;
}
const string& Core::getPushNotificationappId() const {
L_D();
return d->pushNotificationAppId;
}
const string& Core::getPushNotificationToken() const {
L_D();
return d->pushNotificationToken;
}
bool Core::isPushNotificationTimeoutNeeded() {
L_D();
return d->pushNotificationTimeoutNeeded;
}
void Core::setPushNotificationToken(const string& token) {
L_D();
bool newToken = d->pushNotificationToken.empty() || d->pushNotificationToken != token;
if (!newToken) {
return;
}
d->pushNotificationToken = token;
lInfo() << "Push notification token set: " << token;
if (d->pushNotificationType.empty() || d->pushNotificationAppId.empty() || d->pushNotificationToken.empty()) {
return;
}
LinphoneCore *lc = getCCore();
const bctbx_list_t *proxies = linphone_core_get_proxy_config_list(lc);
bctbx_list_t *it = (bctbx_list_t *)proxies;
while (it) {
LinphoneProxyConfig *cfg = (LinphoneProxyConfig *) bctbx_list_get_data(it);
if (linphone_proxy_config_is_push_notification_allowed(cfg)) {
if (cfg->push_notification_allowed) {
const char *token = linphone_core_get_push_notification_token(cfg->lc);
const char *appId = linphone_core_get_push_notification_application_id(cfg->lc);
const char *type = linphone_core_get_push_notification_type(cfg->lc);
const char *ring = linphone_core_get_ring(cfg->lc);
const char *timeout = linphone_core_is_push_notification_timeout_needed(cfg->lc)? ";pn-timeout=0" : "";
if (token && strlen(token) > 0 && appId && strlen(appId) > 0 && type && strlen(type) > 0) {
std::ostringstream ss;
ss << "app-id=" << appId;
ss << ";pn-type=" << type;
ss << ";pn-tok=" << token;
ss << ";pn-msg-str=IM_MSG;pn-call-str=IC_MSG;pn-call-snd=" << ring;
ss << ";pn-msg-snd=msg.caf" << timeout << ";pn-silent=1";
ms_message("Proxy config %s configured for push notifications with contact: %s",
linphone_address_as_string(linphone_proxy_config_get_identity_address(cfg)), ss.str().c_str());
linphone_proxy_config_set_contact_uri_parameters(cfg, ss.str().c_str());
} else {
ms_warning("Proxy config %s NOT configured for push notifications",linphone_address_as_string(linphone_proxy_config_get_identity_address(cfg)));
linphone_proxy_config_set_contact_uri_parameters(cfg, NULL);
}
}
}
it = bctbx_list_next(it);
}
}
void Core::setPushNotificationTimeoutNeeded(bool timeoutNeeded) {
L_D();
d->pushNotificationTimeoutNeeded = timeoutNeeded;
}
void Core::setPushNotificationType(const std::string &type) {
L_D();
d->pushNotificationType = type;
}
void Core::setPushNotificationappId(const std::string &appId) {
L_D();
d->pushNotificationAppId = appId;
}
LINPHONE_END_NAMESPACE
......@@ -185,6 +185,14 @@ public:
Address interpretUrl (const std::string &url) const;
// Execute specified lambda later in main loop. This method can be used from any thread to execute something later on main thread.
void doLater(const std::function<void ()> &something);
const std::string& getPushNotificationType() const;
const std::string& getPushNotificationappId() const;
const std::string& getPushNotificationToken() const;
bool isPushNotificationTimeoutNeeded();
void setPushNotificationToken(const std::string& token);
void setPushNotificationTimeoutNeeded (bool timeoutNeeded);
void setPushNotificationType(const std::string& type);
void setPushNotificationappId(const std::string& appId);
private:
Core ();
......
......@@ -37,6 +37,7 @@
// TODO: Remove me
#include "private.h"
#include "core/PushRegistry.h"
// =============================================================================
using namespace std;
......@@ -54,6 +55,7 @@ public:
void releaseMcastLock () override {}
void acquireCpuLock () override;
void releaseCpuLock () override;
void startPushLongRunningTask(const string &loc_key, const string &callId) override;
string getConfigPath () const override { return ""; }
string getDataPath () const override { return ""; }
......@@ -102,8 +104,10 @@ private:
SCNetworkReachabilityRef reachabilityRef = NULL;
SCNetworkReachabilityFlags mCurrentFlags;
PKPushRegistry *voipRegistry;
static const string Framework;
static void requestPushNotification (void *data);
};
static void sNetworkChangeCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo);
......@@ -116,6 +120,17 @@ IosPlatformHelpers::IosPlatformHelpers (std::shared_ptr<LinphonePrivate::Core> c
mCpuLockCount = 0;
mCpuLockTaskId = 0;
ms_message("[PushKit] Connecting for push notifications");
if (floor(NSFoundationVersionNumber) < NSFoundationVersionNumber_iOS_9_x_Max) {
linphone_core_set_push_notification_timeout_needed(core->getCCore(), false);
}
linphone_core_set_push_notification_type(core->getCCore(), "apple");
voipRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
RegistryDelegate *delegate = [[RegistryDelegate alloc] init];
[delegate setCore:core];
voipRegistry.delegate = delegate;
voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
string cpimPath = getResourceDirPath(Framework, "cpim_grammar");
if (!cpimPath.empty())
belr::GrammarLoader::get().addPath(cpimPath);
......@@ -173,6 +188,28 @@ void IosPlatformHelpers::sBgTaskTimeout (void *data) {
zis->bgTaskTimeout();
}
void IosPlatformHelpers::requestPushNotification (void *data) {
if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
//ms_warning("Incomming message with call-id [%s] couldn't be received", callId.UTF8String);
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = NSLocalizedString(@"Message received", nil);
content.body = NSLocalizedString(@"You have received a message.", nil);
content.categoryIdentifier = @"push_msg";
UNNotificationRequest *req =
[UNNotificationRequest requestWithIdentifier:@"push_msg" content:content trigger:NULL];
[[UNUserNotificationCenter currentNotificationCenter]
addNotificationRequest:req
withCompletionHandler:^(NSError *_Nullable error) {
// Enable or disable features based on authorization.
if (error) {
ms_error("Error while adding notification request :");
ms_error("%s", error.description.UTF8String);
}
}];
}
sBgTaskTimeout(data);
}
// -----------------------------------------------------------------------------
void IosPlatformHelpers::acquireCpuLock () {
......@@ -568,6 +605,27 @@ string IosPlatformHelpers::getWifiSSID(void) {
#endif
}
void IosPlatformHelpers::startPushLongRunningTask(const string &loc_key, const string &callId) {
if (callId.empty())
return;
if (loc_key.compare("IM_MSG") == 0) {
if (mCpuLockCount == 0)
mCpuLockTaskId = static_cast<long>(belle_sip_begin_background_task("request push notification", requestPushNotification, this));
mCpuLockCount++;
ms_message("Message long running task started for call-id [%s], remaining [%f] because a push has been received",
callId.c_str(), [[UIApplication sharedApplication] backgroundTimeRemaining]);
} else if (loc_key.compare("IC_MSG") == 0) {
acquireCpuLock();
ms_message("Call long running task started for call-id [%s], remaining [%f] because a push has been received",
callId.c_str(), [[UIApplication sharedApplication] backgroundTimeRemaining]);
} else if (loc_key.compare("IC_SIL") == 0) {
acquireCpuLock();
ms_message("Refer long running task started for call-id [%s], remaining [%f] because a push has been received",
callId.c_str(), [[UIApplication sharedApplication] backgroundTimeRemaining]);
}
}
// -----------------------------------------------------------------------------
PlatformHelpers *createIosPlatformHelpers(std::shared_ptr<LinphonePrivate::Core> core, void *systemContext) {
......
......@@ -52,6 +52,8 @@ void GenericPlatformHelpers::acquireCpuLock () {}
void GenericPlatformHelpers::releaseCpuLock () {}
void GenericPlatformHelpers::startPushLongRunningTask(const string &loc_key, const string &callId) {}
string GenericPlatformHelpers::getConfigPath () const {
return "";
......
......@@ -49,6 +49,7 @@ public:
virtual void releaseMcastLock () = 0;
virtual void acquireCpuLock () = 0;
virtual void releaseCpuLock () = 0;
virtual void startPushLongRunningTask(const std::string &loc_key, const std::string &callId) = 0;
virtual std::string getConfigPath () const = 0;
virtual std::string getDataPath () const = 0;
......@@ -99,6 +100,7 @@ public:
void releaseMcastLock () override;
void acquireCpuLock () override;
void releaseCpuLock () override;
void startPushLongRunningTask(const std::string &loc_key, const std::string &callId) override;
std::string getConfigPath () const override;
std::string getDataPath () const override;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment