Add Push Notification in ipjsua sample app

This commit is contained in:
sauwming 2024-03-18 12:08:11 +08:00
parent a974061441
commit 609b57c1e4
6 changed files with 192 additions and 20 deletions

View File

@ -42,6 +42,9 @@
3AF0582816F050780046B835 /* ipjsuaViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3AF0582616F050780046B835 /* ipjsuaViewController_iPad.xib */; };
3AF253001EFBD15E00213893 /* libyuv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF252FF1EFBD15E00213893 /* libyuv.a */; };
3AF253021EFBD36E00213893 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF253011EFBD36E00213893 /* VideoToolbox.framework */; };
3AF9B5422B8890880043987D /* PushKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF9B5412B8890880043987D /* PushKit.framework */; };
3AF9B5462B8890F40043987D /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF9B5442B8890F40043987D /* UserNotifications.framework */; };
3AF9B5482BA407AD0043987D /* CallKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF9B5472BA407AD0043987D /* CallKit.framework */; };
7485A6AF1F09AAE500122F1A /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 7485A6AE1F09AAE500122F1A /* Reachability.m */; };
7485A6B11F09B2D500122F1A /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7485A6B01F09B2D500122F1A /* SystemConfiguration.framework */; };
E5E991E61B67A45500017E67 /* libg7221codec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E991D41B67A45500017E67 /* libg7221codec.a */; };
@ -105,6 +108,11 @@
3AF0582716F050780046B835 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ipjsuaViewController_iPad.xib; sourceTree = "<group>"; };
3AF252FF1EFBD15E00213893 /* libyuv.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libyuv.a; sourceTree = "<group>"; };
3AF253011EFBD36E00213893 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; };
3AF9B5402B88896D0043987D /* ipjsua.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ipjsua.entitlements; sourceTree = "<group>"; };
3AF9B5412B8890880043987D /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = System/Library/Frameworks/PushKit.framework; sourceTree = SDKROOT; };
3AF9B5432B8890F40043987D /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; };
3AF9B5442B8890F40043987D /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; };
3AF9B5472BA407AD0043987D /* CallKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CallKit.framework; path = System/Library/Frameworks/CallKit.framework; sourceTree = SDKROOT; };
7485A6AD1F09AAE500122F1A /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = "<group>"; };
7485A6AE1F09AAE500122F1A /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = "<group>"; };
7485A6B01F09B2D500122F1A /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
@ -138,6 +146,7 @@
3AF253021EFBD36E00213893 /* VideoToolbox.framework in Frameworks */,
E5E991EC1B67A45500017E67 /* libpjmedia-codec.a in Frameworks */,
3AA31FF818F3FB4C00112C3D /* CFNetwork.framework in Frameworks */,
3AF9B5422B8890880043987D /* PushKit.framework in Frameworks */,
E5E991E61B67A45500017E67 /* libg7221codec.a in Frameworks */,
E5E991EB1B67A45500017E67 /* libpjmedia-audiodev.a in Frameworks */,
3AA31FFB18F3FB4C00112C3D /* CoreImage.framework in Frameworks */,
@ -152,8 +161,10 @@
E5E991EE1B67A45500017E67 /* libpjmedia.a in Frameworks */,
E5E991EA1B67A45500017E67 /* libpjlib-util.a in Frameworks */,
E5E991ED1B67A45500017E67 /* libpjmedia-videodev.a in Frameworks */,
3AF9B5462B8890F40043987D /* UserNotifications.framework in Frameworks */,
E5E991E81B67A45500017E67 /* libilbccodec.a in Frameworks */,
3A4E3B5B2B6205BA0016735C /* Network.framework in Frameworks */,
3AF9B5482BA407AD0043987D /* CallKit.framework in Frameworks */,
3AA3200118F3FB4C00112C3D /* CoreGraphics.framework in Frameworks */,
3AA31FF918F3FB4C00112C3D /* CoreAudio.framework in Frameworks */,
3AA31FFD18F3FB4C00112C3D /* CoreVideo.framework in Frameworks */,
@ -211,6 +222,10 @@
3AF0580716F050770046B835 /* Frameworks */ = {
isa = PBXGroup;
children = (
3AF9B5472BA407AD0043987D /* CallKit.framework */,
3AF9B5442B8890F40043987D /* UserNotifications.framework */,
3AF9B5432B8890F40043987D /* UserNotificationsUI.framework */,
3AF9B5412B8890880043987D /* PushKit.framework */,
3A4E3B5A2B6205B90016735C /* Network.framework */,
3A4E3B522B61FEAB0016735C /* Metal.framework */,
3A4E3B532B61FEAB0016735C /* MetalKit.framework */,
@ -237,6 +252,7 @@
3AF0580E16F050770046B835 /* ipjsua */ = {
isa = PBXGroup;
children = (
3AF9B5402B88896D0043987D /* ipjsua.entitlements */,
3A92068D16F1D1A100D49F96 /* pjsua */,
3AF0581716F050780046B835 /* ipjsuaAppDelegate.h */,
3AF0581816F050780046B835 /* ipjsuaAppDelegate.m */,
@ -513,7 +529,9 @@
3AF0582C16F050780046B835 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_ENTITLEMENTS = ipjsua/ipjsua.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 93NHJQ455P;
@ -545,10 +563,11 @@
"$(PROJECT_DIR)",
);
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.teluu.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_BUNDLE_IDENTIFIER = "com.teluupush.--PRODUCT-NAME-rfc1034identifier-";
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.teluupush.ipjsua;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Teluu Profile";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "ipjsua Push";
VALID_ARCHS = arm64;
WRAPPER_EXTENSION = app;
};
@ -557,7 +576,9 @@
3AF0582D16F050780046B835 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_ENTITLEMENTS = ipjsua/ipjsua.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 93NHJQ455P;
@ -588,10 +609,11 @@
"$(PROJECT_DIR)",
);
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.teluu.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_BUNDLE_IDENTIFIER = "com.teluupush.--PRODUCT-NAME-rfc1034identifier-";
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.teluupush.ipjsua;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Teluu Profile";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "ipjsua Push";
VALID_ARCHS = arm64;
WRAPPER_EXTENSION = app;
};

View File

@ -4,6 +4,8 @@
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>AppIdentifierPrefix</key>
<string>$(AppIdentifierPrefix)</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>

View File

@ -18,14 +18,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#import <PushKit/PushKit.h>
#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>
@class ipjsuaViewController;
@interface ipjsuaAppDelegate : UIResponder <UIApplicationDelegate>
@interface ipjsuaAppDelegate : UIResponder <UIApplicationDelegate, PKPushRegistryDelegate, UNUserNotificationCenterDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) PKPushRegistry *voipRegistry;
@property (strong, nonatomic) NSMutableString *token;
@property (strong, nonatomic) ipjsuaViewController *viewController;
@end

View File

@ -22,6 +22,7 @@
#import <pjlib.h>
#import <pjsua.h>
#import <pj/log.h>
#import <CallKit/CallKit.h>
#include "../../pjsua_app.h"
#include "../../pjsua_app_config.h"
@ -33,6 +34,14 @@
#define THIS_FILE "ipjsuaAppDelegate.m"
/* Specify if we use push notification. */
#define USE_PUSH_NOTIFICATION 1
/* Account details. */
#define SIP_DOMAIN "sip.pjsip.org"
#define SIP_USER "test"
#define SIP_PASSWD "test"
#define KEEP_ALIVE_INTERVAL 600
ipjsuaAppDelegate *app;
@ -48,18 +57,18 @@ Reachability *internetReach;
BOOL connectionRequired = [curReach connectionRequired];
switch (netStatus) {
case NotReachable:
PJ_LOG(3,("", "Access Not Available.."));
NSLog(@"Access Not Available..");
connectionRequired= NO;
break;
case ReachableViaWiFi:
PJ_LOG(3,("", "Reachable WiFi.."));
NSLog(@"Reachable WiFi..");
break;
case ReachableViaWWAN:
PJ_LOG(3,("", "Reachable WWAN.."));
NSLog(@"Reachable WWAN..");
break;
}
if (connectionRequired) {
PJ_LOG(3,("", "Connection Required"));
NSLog(@"Connection Required");
}
}
@ -68,7 +77,7 @@ Reachability *internetReach;
{
Reachability* curReach = [note object];
NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
PJ_LOG(3,("", "reachability changed.."));
NSLog(@"reachability changed..");
[self updateWithReachability: curReach];
if ([curReach currentReachabilityStatus] != NotReachable &&
@ -76,10 +85,28 @@ Reachability *internetReach;
{
pjsua_ip_change_param param;
pjsua_ip_change_param_default(&param);
pjsua_handle_ip_change(&param);
// pjsua_handle_ip_change(&param);
}
}
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type
{
if ([credentials.token length] == 0) {
NSLog(@"voip token NULL");
return;
}
/* Get device token */
const char *data = [credentials.token bytes];
self.token = [NSMutableString string];
for (NSUInteger i = 0; i < [credentials.token length]; i++) {
[self.token appendFormat:@"%02.2hhx", data[i]];
}
NSLog(@"== VOIP Push Notification Token: %@ ==", self.token);
/* Now start pjsua */
[NSThread detachNewThreadSelector:@selector(pjsuaStart) toTarget:self withObject:nil];
}
void displayLog(const char *msg, int len)
{
@ -171,6 +198,53 @@ static void pjsuaOnAppConfigCb(pjsua_app_config *cfg)
object:[UIDevice currentDevice]];
});
static char contact_uri_buf[1024];
pjsua_acc_config cfg;
pjsua_acc_config_default(&cfg);
cfg.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN);
cfg.reg_uri = pj_str("sip:" SIP_DOMAIN ";transport=tcp");
cfg.cred_count = 1;
cfg.cred_info[0].realm = pj_str(SIP_DOMAIN);
cfg.cred_info[0].scheme = pj_str("digest");
cfg.cred_info[0].username = pj_str(SIP_USER);
cfg.cred_info[0].data = pj_str(SIP_PASSWD);
/* If we have Push Notification token, put it in the registration
* Contact URI params.
*/
if ([self.token length]) {
/* According to RFC 8599:
* - pn-provider is the Push Notification Service provider. Here
* we use APNS (Apple Push Notification Service).
* - pn-param is composed of Team ID and Topic separated by
* a period (.).
* The Topic consists of the Bundle ID, the app's unique ID,
* and the app's service value ("voip" for VoIP apps), separated
* by a period (.).
* - pn-prid is the PN token.
*/
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString *bundleID = infoDictionary[@"CFBundleIdentifier"];
NSString *teamID = infoDictionary[@"AppIdentifierPrefix"];
NSLog(@"Team ID from settings: %@", teamID);
pj_ansi_snprintf(contact_uri_buf, sizeof(contact_uri_buf),
";pn-provider=apns"
";pn-param=%s%s.voip"
";pn-prid=%s",
(teamID? [teamID UTF8String]: "93NHJQ455P."),
[bundleID UTF8String],
[self.token UTF8String]);
cfg.reg_contact_uri_params = pj_str(contact_uri_buf);
}
status = pjsua_acc_add(&cfg, PJ_TRUE, NULL);
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
displayMsg(errmsg);
}
status = pjsua_app_run(PJ_TRUE);
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
@ -187,6 +261,42 @@ static void pjsuaOnAppConfigCb(pjsua_app_config *cfg)
restartArgc = 0;
}
- (void)reportIncomingCall {
CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
/* Report the incoming call to the system using CallKit. */
CXProviderConfiguration *configuration = [[CXProviderConfiguration alloc]
initWithLocalizedName:@"ipjsua"];
CXProvider *provider = [[CXProvider alloc]
initWithConfiguration:configuration];
[provider reportNewIncomingCallWithUUID:[NSUUID UUID] update:callUpdate
completion:^(NSError * _Nullable error)
{
if (error) {
NSLog(@"Error reporting incoming call: %@",
error.localizedDescription);
} else {
NSLog(@"Incoming call reported successfully");
}
}];
}
- (void)pushRegistry:(PKPushRegistry *)registry
didReceiveIncomingPushWithPayload:(PKPushPayload *)payload
forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion
{
/* Handle incoming VoIP push notification. */
NSLog(@"Receiving push notification");
/* Re-register. */
[self performSelectorOnMainThread:@selector(keepAlive) withObject:nil waitUntilDone:YES];
/* Report the incoming call via CallKit. */
[self reportIncomingCall];
/* Call the completion handler when you have finished processing the incoming call. */
completion();
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
@ -198,7 +308,31 @@ static void pjsuaOnAppConfigCb(pjsua_app_config *cfg)
}
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
#if USE_PUSH_NOTIFICATION
/* Set up a push notification registry for Voice over IP (VoIP). */
self.voipRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
self.voipRegistry.delegate = self;
self.voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
/* Request permission for push notifications. */
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert |
UNAuthorizationOptionBadge |
UNAuthorizationOptionSound)
completionHandler:^(BOOL granted, NSError * _Nullable error)
{
NSLog(@"Notification request %sgranted", (granted ? "" : "not"));
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[application registerForRemoteNotifications];
});
}
}];
#endif
/* Observe the kNetworkReachabilityChangedNotification. When that
* notification is posted, the method "reachabilityChanged" will be called.
*/
@ -212,8 +346,12 @@ static void pjsuaOnAppConfigCb(pjsua_app_config *cfg)
app = self;
/* Start pjsua app thread */
#if !USE_PUSH_NOTIFICATION
/* Start pjsua app thread immediately, otherwise we do it after push
* notification setup completes.
*/
[NSThread detachNewThreadSelector:@selector(pjsuaStart) toTarget:self withObject:nil];
#endif
return YES;
}
@ -275,10 +413,6 @@ static void pjsuaOnAppConfigCb(pjsua_app_config *cfg)
pj_thread_register("ipjsua", a_thread_desc, &a_thread);
}
/* Since iOS requires that the minimum keep alive interval is 600s,
* application needs to make sure that the account's registration
* timeout is long enough.
*/
for (i = 0; i < (int)pjsua_acc_get_count(); ++i) {
if (pjsua_acc_is_valid(i)) {
pjsua_acc_set_registration(i, PJ_TRUE);

View File

@ -1597,7 +1597,8 @@ done:
acc->cfg.reg_contact_params.slen +
acc->cfg.reg_contact_uri_params.slen +
(need_outbound?
(acc->rfc5626_instprm.slen + acc->rfc5626_regprm.slen): 0);
(acc->rfc5626_instprm.slen + acc->rfc5626_regprm.slen): 0) +
5; /* allowance */
if (len > acc->contact.slen) {
reg_contact.ptr = (char*) pj_pool_alloc(acc->pool, len);