pjsip (pjsua) notification when remote user answers the call - sip

I am trying to make a simple SIP user agent using https://github.com/pjsip/pjproject. I can succesfully connect to a sip server (Twilio) and place calls to PSTN numbers using the pjsua_* interface. This works fine.
What I would like now is to get a notification (through a callback or such) from pjsip when the user that I am calling answers the call.
I am using on_call_state() to get updates on the invite, but this goes through the same states
CALLING -> CONNECTING -> CONFIRMED -> DISCONNCTD
even if the user rejects the call. So I guess I am not looking at the right callback for this.
How can I definitely tell if the user has answered or rejected the call?

for me it is working this way. in on_call_state callback:
pjsua_call_info callInfo;
pjsua_call_get_info(call_id, &callInfo);
pjsip_inv_state state = callInfo.state;
pjsip_status_code statusCode = callInfo.last_status;
switch (state) {
.....
case PJSIP_INV_STATE_CONFIRMED:
// remote party answered the call normally
break;
case PJSIP_INV_STATE_DISCONNECTED:
if (statusCode == PJSIP_SC_BUSY_HERE) {
// rejected
} else {
// finished ok
}
break;
i reject call this way:
pj_status_t state;
int sendCode = PJSIP_SC_DECLINE;
try {
state = pjsua_call_answer((pjsua_call_id) call_id, sendCode, nullptr, nullptr);
} catch (...) {
return -1;
}
return state;

Related

Flutter Future timeouts not always working correctly

Hey I need some help here for How to use timeouts in flutter correctly. First of all to explain what the main goal is:
I want to recive data from my Firebase RealTime Database but need to secure this request api call with an time out of 15 sec. So after 15 sec my timeout should throw an exception that will return to the Users frontend the alert for reasons of time out.
So I used the simple way to call timeouts on future functions:
This functions should only check if on some firebase node an ID is existing or not:
Inside this class where I have declared this functions I also have an instance which called : timeoutControl this is a class which contains a duration and some reasons for the exceptions.
Future<bool> isUserCheckedIn(String oid, String maybeCheckedInUserIdentifier, String onGateId) async {
try {
databaseReference = _firebaseDatabase.ref("Boarding").child(oid).child(onGateId);
final snapshot = await databaseReference.get().timeout(Duration(seconds: timeoutControl.durationForTimeOutInSec), onTimeout: () => timeoutControl.onEppTimeoutForTask());
if(snapshot.hasChild(maybeCheckedInUserIdentifier)) {
return true;
}
else {
return false;
}
}
catch (exception) {
return false;
}
}
The TimeOutClass where the instance timeoutControl comes from:
class CustomTimeouts {
int durationForTimeOutInSec = 15; // The seconds for how long to try until we throw an timeout exception
CustomTimeouts();
// TODO: Implement the exception reasons here later ...
onEppTimeoutForUpload() {
throw Exception("Some reason ...");
}
onEppTimeoutForTask() {
throw Exception("Some reason ...");
}
onEppTimeoutForDownload() {
throw Exception("Some reason ...");
}
}
So as you can see for example I tried to use this implementation above. This works fine ... sometimes I need to fight with un explain able things -_-. Let me try to introduce what in somecases are the problem:
Inside the frontend class make this call:
bool isUserCheckedIn = await service.isUserCheckedIn(placeIdentifier, userId, gateId);
Map<String, dynamic> data = {"gateIdActive" : isUserCheckedIn};
/*
The response here is an Custom transaction handler which contains an error or an returned param
etc. so this isn't relevant for you ...
*/
_gateService.updateGate(placeIdentifier, gateId, data).then((response) {
if(response.hasError()) {
setState(() {
EppDialog.showErrorToast(response.getErrorMessage()); // Shows an error message
isSendButtonDiabled = false; /*Reset buttons state*/
});
}
else {
// Create an gate process here ...
createGateEntrys(); // <-- If the closures update was successful we also handle some
// other data inside the RTDB for other reasons here ...
}
});
IMPORTANT to know for you guys is that I am gonna use the returned "boolean" value from this function call to update some other data which will be pushed and uploaded into another RTDB other node location for other reasons. And if this was also successful the application is going on to update some entrys also inside the RTDB -->createGateEntrys()<-- This function is called as the last one and is also marked as an async function and called with its closures context and no await statement.
The Data inside my Firebase RTDB:
"GateCheckIns" / "4mrithabdaofgnL39238nH" (The place identifier) / "NFdxcfadaies45a" (The Gate Identifier)/ "nHz2mhagadzadzgadHjoeua334" : 1 (as top of the key some users id who is checked in)
So on real devices this works always without any problems... But the case of an real device or simulator could not be the reason why I'am faceing with this problem now. Sometimes inside the Simulator this Function returns always false no matter if the currentUsers Identifier is inside the this child nodes or not. Therefore I realized the timeout is always called immediately so right after 1-2 sec because the exception was always one of these I was calling from my CustomTimeouts class and the function which throws the exception inside the .timeout(duration, onTimeout: () => ...) call. I couldn't figure it out because as I said on real devices I was not faceing with this problem.
Hope I was able to explain the problem it's a little bit complicated I know but for me is important that someone could explain me for what should I pay attention to if I am useing timeouts in this style etc.
( This is my first question here on StackOverFlow :) )

OPC UA Client capture the lost item values from the UA server after a disconnect/connection error?

I am building a OPC UA Client using OPC Foundation SDK. I am able to create a subscription containing some Monitoreditems.
On the OPC UA server these monitored items change value constantly (every second or so).
I want to disconnect the client (simulate a connection broken ), keep the subcription alive and wait for a while. Then I reconnect having my subscriptions back, but I also want all the monitored Item values queued up during the disconnect. Right now I only get the last server value on reconnect.
I am setting a queuesize:
monitoredItem.QueueSize = 100;
To kind of simulate a connection error I have set the "delete subscription" to false on ClosesSession:
m_session.CloseSession(new RequestHeader(), false);
My question is how to capture the content of the queue after a disconnect/connection error???
Should the ‘lost values’ be “new MonitoredItem_Notification” automatically when the client reconnect?
Should the SubscriptionId be the same as before the connection was broken?
Should the sessionId be the same or will a new SessionId let med keep the existing subscriptions? What is the best way to simulate a connection error?
Many questions :-)
A sample from the code where I create the subscription containing some MonitoredItems and the MonitoredItem_Notification event method.
Any OPC UA Guru out there??
if (node.Displayname == "node to monitor")
{
MonitoredItem mon = CreateMonitoredItem((NodeId)node.reference.NodeId, node.Displayname);
m_subscription.AddItem(mon);
m_subscription.ApplyChanges();
}
private MonitoredItem CreateMonitoredItem(NodeId nodeId, string displayName)
{
if (m_subscription == null)
{
m_subscription = new Subscription(m_session.DefaultSubscription);
m_subscription.PublishingEnabled = true;
m_subscription.PublishingInterval = 3000;//1000;
m_subscription.KeepAliveCount = 10;
m_subscription.LifetimeCount = 10;
m_subscription.MaxNotificationsPerPublish = 1000;
m_subscription.Priority = 100;
bool cache = m_subscription.DisableMonitoredItemCache;
m_session.AddSubscription(m_subscription);
m_subscription.Create();
}
// add the new monitored item.
MonitoredItem monitoredItem = new MonitoredItem(m_subscription.DefaultItem);
//Each time a monitored item is sampled, the server evaluates the sample using a filter defined for each monitoreditem.
//The server uses the filter to determine if the sample should be reported. The type of filter is dependent on the type of item.
//DataChangeFilter for Variable, Eventfilter when monitoring Events. etc
//MonitoringFilter f = new MonitoringFilter();
//DataChangeFilter f = new DataChangeFilter();
//f.DeadbandValue
monitoredItem.StartNodeId = nodeId;
monitoredItem.AttributeId = Attributes.Value;
monitoredItem.DisplayName = displayName;
//Disabled, Sampling, (Report (includes sampling))
monitoredItem.MonitoringMode = MonitoringMode.Reporting;
//How often the Client wish to check for new values on the server. Must be 0 if item is an event.
//If a negative number the SamplingInterval is set equal to the PublishingInterval (inherited)
//The Subscriptions KeepAliveCount should always be longer than the SamplingInterval/PublishingInterval
monitoredItem.SamplingInterval = 500;
//Number of samples stored on the server between each reporting
monitoredItem.QueueSize = 100;
monitoredItem.DiscardOldest = true;//Discard oldest values when full
monitoredItem.CacheQueueSize = 100;
monitoredItem.Notification += m_MonitoredItem_Notification;
if (ServiceResult.IsBad(monitoredItem.Status.Error))
{
return null;
}
return monitoredItem;
}
private void MonitoredItem_Notification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new MonitoredItemNotificationEventHandler(MonitoredItem_Notification), monitoredItem, e);
return;
}
try
{
if (m_session == null)
{
return;
}
MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
if (notification == null)
{
return;
}
string sess = m_session.SessionId.Identifier.ToString();
string s = string.Format(" MonitoredItem: {0}\t Value: {1}\t Status: {2}\t SourceTimeStamp: {3}", monitoredItem.DisplayName, (notification.Value.WrappedValue.ToString().Length == 1) ? notification.Value.WrappedValue.ToString() : notification.Value.WrappedValue.ToString(), notification.Value.StatusCode.ToString(), notification.Value.SourceTimestamp.ToLocalTime().ToString("HH:mm:ss.fff"));
richTextBox1.AppendText(s + "SessionId: " + sess);
}
catch (Exception exception)
{
ClientUtils.HandleException(this.Text, exception);
}
}e here
I don't know how much of this, if any, the SDK you're using does for you, but the approach when reconnecting is generally:
try to resume (re-activate) your old session. If this is successful your subscriptions will already exist and all you need to do is send more PublishRequests. Since you're trying to test by closing the session this probably won't work.
create a new session and then call the TransferSubscription service to transfer the previous subscriptions to your new session. You can then start sending PublishRequests and you'll get the queued notifications.
Again, depending on the stack/SDK/toolkit you're using some or none of this may be handled for you.

Wifi Direct - GO receives connection request

I have 2 P2P devices (android smartphones). They created a Wifi Direct Group. So, we have a Group owner and a Client. A third device appears, and wants to join the group sending a connection request to the GO. When this happens the GO BroadcastReceiver detects a disconnect. I would like to know if that is a normal behavior or I'm doing something wrong.
This is part of my BroadcastReceiver
else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION
.equals(action)) {
if (manager == null) {
return;
}
NetworkInfo networkInfo = (NetworkInfo) intent
.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
WifiP2pInfo wifiP2pInfo = (WifiP2pInfo) intent
.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
WifiP2pGroup wifiP2pGroup = (WifiP2pGroup) intent
.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
if (networkInfo.isConnected()) {
// we are connected with the other device, request connection
// info to find group owner IP
} else {
// It's a disconnect
}
} else if ....

How to unregister from SIP in linphone?

Is there a way to unregister from SIP and re-register when I want in Linphone?
I can't find the unregister function.
Should I destroy the linphone core completely for that?
Or is there a more soft solution?
Currently I am trying to implement it in iOS, but later this will be required for additional platforms.
Thank you.
// Get the default proxyCfg in Linphone
LinphoneProxyConfig* proxyCfg = NULL;
linphone_core_get_default_proxy([LinphoneManager getLc], &proxyCfg);
// To unregister from SIP
linphone_proxy_config_edit(proxyCfg);
linphone_proxy_config_enable_register(proxyCfg, false);
linphone_proxy_config_done(proxyCfg);
// And re-register when want
linphone_proxy_config_edit(proxyCfg);
linphone_proxy_config_enable_register(proxyCfg, true);
linphone_proxy_config_done(proxyCfg);
You may have multiple SIP accounts. First, specify the account you want and get your proxy from getProxyConfigList. Then remove the proxy you want from the core of the linphone via removeProxyConfig method:
private static AuthInfo mAuthInfo;
private static ProxyConfig mProxyConfig;
Core core = LinphoneManager.getCore();
ProxyConfig[] proxyConfigs = core.getProxyConfigList();
if (proxyConfigs.length != 0) {
mProxyConfig = proxyConfigs[0];
mAuthInfo = mProxyConfig.findAuthInfo();
}
if (core != null) {
{
if (mProxyConfig != null) {
core.removeProxyConfig(mProxyConfig);
}
if (mAuthInfo != null) {
core.removeAuthInfo(mAuthInfo);
}
}
there is the way how to unregister using linphone.
Get the LinphoneProxyConfig
LinphoneProxyConfig* proxyCfg = NULL;
linphone_core_get_default_proxy([LinphoneManager getLc], &proxyCfg);
To unregister from SIP
linphone_proxy_config_edit(proxyCfg); /*start editing proxy configuration*/
linphone_proxy_config_enable_publish(proxyCfg, TRUE);
linphone_proxy_config_set_publish_expires(proxyCfg, 0);
linphone_proxy_config_enable_register(proxyCfg,FALSE); /*de-activate registration for this proxy config*/
linphone_proxy_config_done(proxyCfg); /*initiate REGISTER with expire = 0*/
while(linphone_proxy_config_get_state(proxyCfg) != LinphoneRegistrationCleared){
NSLog(#"state = %i",linphone_proxy_config_get_state(proxyCfg));
linphone_core_iterate(lc); /*to make sure we receive call backs before shutting down*/
ms_usleep(100000);
}
But it's works only when app in Foreground. In background if the OS kills your app for any reason, it is killed. There is no notification. You can't catch the SIGKILL signal. Look at the man page for kill.
Unregistration not exactly given. Remove sip extension and then call refresh registers. Now your softphone unregistered from sip
[[NSUserDefaults standardUserDefaults] setObject:#"" forKey:#"username_preference"];
if ([[UIDevice currentDevice] respondsToSelector:#selector(isMultitaskingSupported)]
&& [UIApplication sharedApplication].applicationState == UIApplicationStateBackground
&& [[NSUserDefaults standardUserDefaults] boolForKey:#"disable_autoboot_preference"]) {
// autoboot disabled, doing nothing
return;
} else if ([SipManager instance] == nil) {
[self startApplication:caldelegate];
}
[[LinphoneManager instance] becomeActive];
if (callCenter == nil) {
callCenter = [[CTCallCenter alloc] init];
callCenter.callEventHandler = ^(CTCall* call) {
// post on main thread
[self performSelectorOnMainThread:#selector(handleGSMCallInteration:)
withObject:callCenter
waitUntilDone:YES];
};
}
// check call state at startup
[self handleGSMCallInteration:callCenter];
LinphoneCore* lc = [SipManager getLc];
LinphoneCall* call = linphone_core_get_current_call(lc);
if (call == NULL)
return;
SipManager* instance = [SipManager instance];
if (call == instance->currentCallContextBeforeGoingBackground.call) {
const LinphoneCallParams* params = linphone_call_get_current_params(call);
if (linphone_call_params_video_enabled(params)) {
linphone_call_enable_camera(
call,
instance->currentCallContextBeforeGoingBackground.cameraIsEnabled);
}
instance->currentCallContextBeforeGoingBackground.call = 0;
}
Actually, there is no "unregistration" option for a sip server. The location server will update to the newest register information (including your newest IP address).
If you are talking about how to stop linphone iterate registration, and re-register to the other SIP server. Then follow the #Mun Chun's guide
LinphoneCore *lc = [LinphoneManager getLc];
LinphoneProxyConfig *config = linphone_core_get_default_proxy_config(lc);
linphone_proxy_config_edit(config);
linphone_proxy_config_set_expires(config, 0);
linphone_proxy_config_done(config);
I've worked on Linphone for some months and my function to unregister from SIP was:
- (void)clearProxies {
LinphoneProxyConfig *config = linphone_core_get_default_proxy_config(LC); // Get the default proxy configured.
const LinphoneAuthInfo *ai = linphone_proxy_config_find_auth_info(config);
linphone_core_remove_proxy_config(LC, config); // Remove the selected proxy config.
if (ai) {
linphone_core_remove_auth_info(LC, ai); // Remove the authentication infos.
}
linphone_proxy_config_done(config); // Confirm the actual configuration.
}
You have to clear both proxy config and auth info and confirm the new configuration.

Multiple XMPP BOSH Connections Interfering in Internet Explorer - UPDATE: Seen In Other Browsers

Introduction
I am working on an MVC web app that is using XMPP for two purposes:
1) Communicating real time data from the server to the client - XMPPMessaging
2) User group chat - XMPPChat
I am using Strophe.js on the client side and MatriX C# library on the server side. XMPPChat is being set up by prebinding on the server side and passing the connection info to the client, which attaches with strophe. XMPPMessaging is not prebinding, just logging in on the client side. Every window that the user opens connects a new XMPPMessaging instance to receive data from the server. Only the chat windows connect XMPPChat and XMPPMessaging. Also, both are connecting to the Punjab connection manager over BOSH, which is forwarding the messages to an Openfire server.
Issue
Currently, I have an issue that only manifests itself in Internet Explorer (I am testing with IE8 and IE9 and both exibit this issue). When I have XMPPMessaging and XMPPChat connected, chat messages are being sent after a very large delay (10 sec - 2 minutes). I've seen this cause disconnection issues for XMPPChat as well. The receive functionality works great; if any other browser posts to the chatroom, IE get's it immediately just like everyone else. When XMPPMessaging is turned off, the chat send runs great with no delays or disconnections. I've stepped through the code with breakpoints and the send functionality is firing immediately. I've also observed the Punjab logs, and it appears that Punjab isn't getting the message till after the delay and is passing them along fine (also evidenced by the other browsers working fine).
I'm mostly wondering if anyone else has seen issues like this one with concurrent BOSH connections in IE comehow interfering.
Update
The chat feature in IE started working today completely inexplicably; I haven't changed the code or any configuration of it at all. I double checked that the messaging connection and chat connection were both running, and they both were connected. In attempts to diagnose the fix, I restarted Apache and the bug came back, again only in IE we saw the chat slow down significantly and randomly disconnect.
Update 2 - SEEN IN OTHER BROWSERS
Today I was able to recreate the issue in other browsers (Chrome, Firefox) by opening multiple chat instances in other tabs. So it seems that IE only suffered worse from the issue - only needing one tab open. This is pointing me towards a browser concurrent connection limit issue, but that doesn't make sense to be either considering that I shouldn't be anywhere near the limit with 2 connections.
Update 3 - CAUSE DETERMINED IN OTHER BROWSERS
I performed a test in Firefox to confirm my suspicions that we are dealing with a concurrent connection issue. I opened one chat tab and the messages posted instantaneously. I opened a second one and, as expected, they delayed. I then opened about:config and changed network.http.max-persistent-connections-per-server from 6 to 7. After doing this I repeated the test. I was able to post messages instantaneously in both the first and second chat tabs, but when I opened a third they all began experiencing the delay. I'm going to try and test in IE to see if this is the same problem by modifying the FEATURE_MAXCONNECTIONSPERSERVER registry settings and seeing if the problem goes away.
Update 4 - IE ISSUE STILL NOT RESOLVED
I performed the same test in IE, changing the registry as shown here, adding iexplorer.exe DWORD settings to the FEATURE_MAXCONNECTIONSPERSERVER and FEATURE_MAXCONNECTIONSPER1_0SERVER registries set to 0xA, but no changes in behavior are apparent. I tried the same thing with the x64 settings (under Wow6432Node) and still observed no changes. I also tried changing the original explorer.exe values from 2 or 4 to 8, but still saw no changes in behavior, all after restarting the computer after each change.
Code
For reference, here is some of my XMPP chat code which may or may not be relevant:
XMPPAttach: function (jid, sid, rid) {
connection = new Strophe.Connection(BOSH_SERVICE);
connection.rawInput = function (data) {
log('RECV: ' + data);
};
connection.rawOutput = function (data) {
log('SENT: ' + data);
};
MY_JID = jid;
connection.addHandler(notifyUser, null, 'message', 'chat', null, null);
connection.addHandler(groupChat, null, 'message', 'groupchat', null, null);
connection.addHandler(presenceInfo, null, 'presence', null, null, null);
connection.ping.addPingHandler(pingHandler);
connection.attach(jid, sid, rid, onConnect, 300);
}
function onConnect(status) {
switch (status) {
case Strophe.Status.CONNECTED:
log('CONNECTED');
break;
case Strophe.Status.ERROR:
log('ERROR');
break;
case Strophe.Status.CONNFAIL:
log('CONNFAIL');
break;
case Strophe.Status.AUTHENTICATING:
log('AUTHENTICATING');
break;
case Strophe.Status.AUTHFAIL:
log('AUTHFAIL');
break;
case Strophe.Status.CONNECTING:
log('CONNECTING');
break;
case Strophe.Status.DISCONNECTED:
log('DISCONNECTED');
break;
case Strophe.Status.DISCONNECTING:
log('DISCONNECTING');
break;
case Strophe.Status.ATTACHED:
log('ATTACHED');
break;
default:
log('UKNOWN STATUS CODE');
break;
}
if ((status == Strophe.Status.CONNECTED || status == Strophe.Status.ATTACHED) && !presenceSent) {
connection.send($pres().tree());
presenceSent = true;
userLogin();
}
else if (status == Strophe.Status.CONNECTING || status == Strophe.Status.AUTHENTICATING) {
// do nothing
}
else if (status == Strophe.Status.AUTHFAIL) {
userInvalidLogin();
}
else {
userLoginFailed();
}
}
EnterChatRoom: function (room, nick) {
ROOM_JID = room;
MY_NICK = nick;
var chatJID = room.concat('/', nick);
var pres = $pres({ to: chatJID }).c('x', { xmlns: 'http://jabber.org/protocol/muc' });
connection.send(pres);
}
SendToRoom: function (text) {
var send = $msg({ to: ROOM_JID, type: 'groupchat' }).c('body', {}, text);
connection.send(send.tree());
},
And here is some of my XMPP messaging code:
function initializeXMPP() {
connection = new Strophe.Connection(BOSH_SERVICE);
intentionalDisconnect = false;
connection.rawInput = function (data) {
log('RECV: ' + data);
};
connection.rawOutput = function (data) {
log('SENT: ' + data);
};
connection.addHandler(onMessage, null, 'message', null, null, null);
connection.ping.addPingHandler(pingHandler);
connection.connect(messageCatcher.serverSettings.clientUser + '#' + messageCatcher.serverSettings.xmppDomain,
messageCatcher.serverSettings.clientUserPassword,
onConnect, 300, undefined, messageCatcher.serverSettings.route);
log('Strophe is connected.');
}
function onConnect(status) {
switch (status) {
case Strophe.Status.ERROR: // 0
log('ERROR');
StandardErrorHandler({ "Message": "XMPP Connection Error", "Status": "ERROR" });
break;
case Strophe.Status.CONNECTING: // 1
log('CONNECTING');
break;
case Strophe.Status.CONNFAIL: // 2
log('CONNFAIL');
StandardErrorHandler({ "Message": "XMPP Connection Error", "Status": "CONNFAIL" });
break;
case Strophe.Status.AUTHENTICATING: // 3
log('AUTHENTICATING');
break;
case Strophe.Status.AUTHFAIL: // 4
log('AUTHFAIL');
StandardErrorHandler("XMPP AUTHFAIL");
break;
case Strophe.Status.CONNECTED: // 5
log('CONNECTED');
callHandlers(connectHandlers);
break;
case Strophe.Status.DISCONNECTED: // 6
log('DISCONNECTED');
onDisconnect();
break;
case Strophe.Status.DISCONNECTING: // 7
log('DISCONNECTING');
break;
case Strophe.Status.ATTACHED: // 8
log('ATTACHED');
break;
default:
log('UKNOWN STATUS CODE');
break;
}
if (status == Strophe.Status.CONNECTED && !presenceSent) {
var pres = $pres({
//type: 'available'
});
connection.send(pres);
presenceSent = true;
log("PRESENCE SENT - SID: " + connection.sid);
}
}