[RFC] Add Message Waiting Indication to sipdroid

88 views
Skip to first unread message

James Bottomley

unread,
Oct 5, 2009, 12:22:30 PM10/5/09
to sipdroid-...@googlegroups.com
One of the nice features about SIP phones is that they can subscribe to
the voicemail box and tell you if anything is in it. This code adds a
standard messages-summary subscription to sipdroid and keys the
voicemail notifier off it (if the sip system supports, it will also tell
you how many messages you have waiting).

The code is a bit rough in that it needs a voicemail call number so that
when you hit the voicemail notifier it actually goes to your mailbox
instead of the sipdroid dial something dialogue. It also looks like we
could share the authentication code between the subscribe and register
messages (this would give subscribe proxy auth as well).

James

---

Index: src/org/sipdroid/sipua/SipdroidEngine.java
===================================================================
--- src/org/sipdroid/sipua/SipdroidEngine.java (revision 329)
+++ src/org/sipdroid/sipua/SipdroidEngine.java (working copy)
@@ -212,6 +212,18 @@
wl.release();
}

+ public void onMWIUpdate(boolean voicemail, int number) {
+ if (voicemail) {
+ String msgs = getUIContext().getString(R.string.voicemail);
+ if (number != 0) {
+ msgs = msgs + ": " + number;
+ }
+ Receiver.onText(Receiver.MWI_NOTIFICATION, msgs,android.R.drawable.stat_notify_voicemail,0);
+ } else {
+ Receiver.onText(Receiver.MWI_NOTIFICATION, null, 0,0);
+ }
+ }
+
static long lasthalt;

/** When a UA failed on (un)registering. */
Index: src/org/sipdroid/sipua/RegisterAgent.java
===================================================================
--- src/org/sipdroid/sipua/RegisterAgent.java (revision 329)
+++ src/org/sipdroid/sipua/RegisterAgent.java (working copy)
@@ -27,6 +27,8 @@
import org.sipdroid.sipua.ui.Sipdroid;
import org.zoolu.sip.address.NameAddress;
import org.zoolu.sip.authentication.DigestAuthentication;
+import org.zoolu.sip.dialog.SubscriberDialog;
+import org.zoolu.sip.dialog.SubscriberDialogListener;
import org.zoolu.sip.header.AuthorizationHeader;
import org.zoolu.sip.header.ContactHeader;
import org.zoolu.sip.header.ExpiresHeader;
@@ -44,12 +46,13 @@
import org.zoolu.sip.transaction.TransactionClientListener;
import org.zoolu.tools.Log;
import org.zoolu.tools.LogLevel;
+import org.zoolu.tools.Parser;

/**
* Register User Agent. It registers (one time or periodically) a contact
* address with a registrar server.
*/
-public class RegisterAgent implements TransactionClientListener {
+public class RegisterAgent implements TransactionClientListener, SubscriberDialogListener {
/** Max number of registration attempts. */
static final int MAX_ATTEMPTS = 3;

@@ -103,6 +106,10 @@

UserAgentProfile user_profile;

+ SubscriberDialog sd;
+ Message currentSubscribeMessage;
+ public final int SUBSCRIPTION_EXPIRES = 184000;
+
/**
* Creates a new RegisterAgent with authentication credentials (i.e.
* username, realm, and passwd).
@@ -223,9 +230,151 @@

/** Unregister with the registrar server */
public boolean unregister() {
+ stopMWI();
return register(0);
}

+ void stopMWI()
+ {
+ if (sd != null) {
+ synchronized (sd) {
+ sd.notify();
+ }
+ }
+ listener.onMWIUpdate(false, 0);
+ }
+
+ Message getSubscribeMessage(boolean current)
+ {
+ String empty = null;
+ Message req;
+
+ // Need to restart subscriber dialogue state engine
+ if (sd != null) {
+ synchronized (sd) {
+ sd.notify();
+ }
+ }
+ sd = new SubscriberDialog(sip_provider, "message-summary", "", this);
+ if (current) {
+ req = currentSubscribeMessage;
+ req.setCSeqHeader(req.getCSeqHeader().incSequenceNumber());
+ } else {
+ req = MessageFactory.createSubscribeRequest(sip_provider,
+ target.getAddress(), target, target,
+ contact, sd.getEvent(),
+ sd.getId(), empty, empty);
+ }
+ req.setExpiresHeader(new ExpiresHeader(SUBSCRIPTION_EXPIRES));
+ currentSubscribeMessage = req;
+ return req;
+ }
+
+
+ void startMWI()
+ {
+ Message req = getSubscribeMessage(false);
+ sd.subscribe(req);
+ }
+
+ void delayStartMWI()
+ {
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ Object o = new Object();
+ try {
+ synchronized (o) {
+ o.wait(10000);
+ }
+ } catch (Exception E) {
+ }
+ startMWI();
+ }
+ });
+ t.start();
+ }
+
+ // **************** Subscription callback functions *****************
+ public void onDlgSubscriptionSuccess(SubscriberDialog dialog, int code,
+ String reason, Message resp)
+ {
+ final int expires;
+ if (resp.hasExpiresHeader()) {
+ expires = resp.getExpiresHeader().getDeltaSeconds();
+ } else {
+ expires = SUBSCRIPTION_EXPIRES;
+ }
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ try {
+ synchronized (sd) {
+ sd.wait(expires*1000);
+ }
+ startMWI();
+ } catch(Exception E) {
+ }
+ }
+ });
+ t.start();
+ }
+
+ public void onDlgSubscriptionFailure(SubscriberDialog dialog, int code,
+ String reason, Message resp)
+ {
+ if (code == 401) {
+ Message req = getSubscribeMessage(true);
+ WwwAuthenticateHeader wah = resp.getWwwAuthenticateHeader();
+ String qop_options = wah.getQopOptionsParam();
+
+
+ qop = (qop_options != null) ? "auth" : null;
+
+ AuthorizationHeader ah = (new DigestAuthentication(
+ SipMethods.SUBSCRIBE, req.getRequestLine().getAddress()
+ .toString(), wah, qop, null, username, passwd))
+ .getAuthorizationHeader();
+ req.setAuthorizationHeader(ah);
+ sd.subscribe(req);
+ } else {
+ delayStartMWI();
+ }
+ }
+
+ public void onDlgSubscribeTimeout(SubscriberDialog dialog)
+ {
+ delayStartMWI();
+ }
+
+ public void onDlgSubscriptionTerminated(SubscriberDialog dialog)
+ {
+ startMWI();
+ }
+
+ public void onDlgNotify(SubscriberDialog dialog, NameAddress target,
+ NameAddress notifier, NameAddress contact, String state,
+ String content_type, String body, Message msg)
+ {
+ Parser p = new Parser(body);
+ final char[] propertysep = { ':', '\r', '\n' };
+ final char[] vmailsep = { '/' };
+ boolean voicemail = false;
+ int nummsg = 0;
+ while (p.hasMore()) {
+ String property = p.getWord(propertysep);
+ p.skipChar();
+ p.skipWSP();
+ String value = p.getWord(p.CRLF);
+ if (property.equalsIgnoreCase("Messages-Waiting") && value.equalsIgnoreCase("yes")) {
+ voicemail = true;
+ } else if (property.equalsIgnoreCase("Voice-Message")) {
+ Parser np = new Parser(value);
+ String num = np.getWord(vmailsep);
+ nummsg = Integer.parseInt(num);
+ }
+ }
+ listener.onMWIUpdate(voicemail, nummsg);
+ }
+
// **************** Transaction callback functions *****************

/** Callback function called when client sends back a failure response. */
@@ -274,6 +423,7 @@
if (listener != null)
{
listener.onUaRegistrationSuccess(this, target, contact, result);
+ startMWI();
Receiver.reRegister(expires);
}
}
@@ -306,6 +456,7 @@
{
listener.onUaRegistrationFailure(this, target, contact,
result);
+ stopMWI();
Receiver.reRegister(1000);
}
}
@@ -316,6 +467,7 @@
{
listener.onUaRegistrationFailure(this, target, contact,
result);
+ stopMWI();
}
}

@@ -412,6 +564,7 @@
{
listener.onUaRegistrationFailure(this, target, contact,
"Timeout");
+ stopMWI();
Receiver.reRegister(1000);
}
}
@@ -422,6 +575,7 @@
{
listener.onUaRegistrationFailure(this, target, contact,
"Timeout");
+ stopMWI();
}
}
}
Index: src/org/sipdroid/sipua/RegisterAgentListener.java
===================================================================
--- src/org/sipdroid/sipua/RegisterAgentListener.java (revision 329)
+++ src/org/sipdroid/sipua/RegisterAgentListener.java (working copy)
@@ -29,6 +29,8 @@
public void onUaRegistrationSuccess(RegisterAgent ra, NameAddress target,
NameAddress contact, String result);

+ public void onMWIUpdate(boolean voicemail, int number);
+
/** When a UA failed on (un)registering. */
public void onUaRegistrationFailure(RegisterAgent ra, NameAddress target,
NameAddress contact, String result);
Index: src/org/sipdroid/sipua/ui/Receiver.java
===================================================================
--- src/org/sipdroid/sipua/ui/Receiver.java (revision 329)
+++ src/org/sipdroid/sipua/ui/Receiver.java (working copy)
@@ -69,6 +69,7 @@
public final static int CALL_NOTIFICATION = 2;
public final static int MISSED_CALL_NOTIFICATION = 3;
public final static int AUTO_ANSWER_NOTIFICATION = 4;
+ public final static int MWI_NOTIFICATION = 5;

final static long[] vibratePattern = {0,1000,1000};

Index: res/values/strings.xml
===================================================================
--- res/values/strings.xml (revision 329)
+++ res/values/strings.xml (working copy)
@@ -51,6 +51,8 @@
<string name="regok">Registered</string>
<string name="regfailed">Registration failed</string>

+<string name="voicemail">Message(s) Waiting</string>
+
<string name="auto_enabled">Auto-Answer/Speakerphone</string>
<string name="auto_disabled">Auto-Answer off</string>
<string name="hint">Called Party Address</string>


Pascal Merle

unread,
Oct 5, 2009, 4:19:52 PM10/5/09
to Sipdroid Developers
Please give me your Google Code ID to add it to the members' list.

When checking this in please add an advanced option (defaulting to on)
for MWI display. I would like to be able to turn this off because I
receive my voicemail over email and two notifications would be too
much.

Also please complete the patch to call to the voicemail number if the
notification is clicked on.

James Bottomley

unread,
Oct 5, 2009, 4:29:57 PM10/5/09
to sipdroid-...@googlegroups.com
On Mon, 2009-10-05 at 13:19 -0700, Pascal Merle wrote:
> Please give me your Google Code ID to add it to the members' list.

What's a google code id? (Google search doesn't turn up anything useful)

> When checking this in please add an advanced option (defaulting to on)
> for MWI display. I would like to be able to turn this off because I
> receive my voicemail over email and two notifications would be too
> much.

Sure thing ... I was debating adding this since some sip servers do
silly things with MWI subscriptions.

> Also please complete the patch to call to the voicemail number if the
> notification is clicked on.

Will do ... it's annoying me as I use the patch, so I will add this.

James


Pascal Merle

unread,
Oct 5, 2009, 5:02:57 PM10/5/09
to Sipdroid Developers
I have not fully understood what a google code id is either. In most
cases it is just your email address @gmail.com.

James Bottomley

unread,
Oct 5, 2009, 6:19:47 PM10/5/09
to sipdroid-...@googlegroups.com
On Mon, 2009-10-05 at 14:02 -0700, Pascal Merle wrote:
> I have not fully understood what a google code id is either. In most
> cases it is just your email address @gmail.com.

Oh ... ok, I really don't have one of those. I created a gmail account
for android and then disabled it again ... proliferation of mailbox
problems and all. The address above is what I use to login to the
google site, so it's likely what you need.

James


Pascal Merle

unread,
Oct 6, 2009, 6:47:16 AM10/6/09
to Sipdroid Developers
Have you ever posted a comment or an issue on Google Code? Then it
displays your ID next to it. I tried to add jejbgo@hens... but it
converted it to jejbgo@gmail.

James Bottomley

unread,
Oct 7, 2009, 9:32:43 AM10/7/09
to sipdroid-...@googlegroups.com
One of the nice features about SIP phones is that they can subscribe to
the voicemail box and tell you if anything is in it. This code adds a
standard messages-summary subscription to sipdroid and keys the
voicemail notifier off it (if the sip system supports, it will also tell
you how many messages you have waiting).

This version has a preference to disable using SIP MWI plus a setting
for the voicemail box to call when you click on the notification. This
patch also abstracts the authentication handling so both register and
subscribe can use it.

There was one problem that showed up in testing. It seems that during a
call, the onDlgSubscribeSuccess() notification gets called spuriously
(lots of times). I fixed this by simply tracking an alreadySubscribed
state and ignoring the notification of success if we're subscribed.

James

---

res/values/strings.xml | 6
res/xml/preferences.xml | 8
src/org/sipdroid/sipua/RegisterAgent.java | 189 ++++++++++++++++++++--
src/org/sipdroid/sipua/RegisterAgentListener.java | 2
src/org/sipdroid/sipua/SipdroidEngine.java | 16 +
src/org/sipdroid/sipua/ui/Receiver.java | 15 +
src/org/sipdroid/sipua/ui/Settings.java | 4
7 files changed, 220 insertions(+), 20 deletions(-)


Index: src/org/sipdroid/sipua/SipdroidEngine.java
===================================================================
--- src/org/sipdroid/sipua/SipdroidEngine.java (revision 329)
+++ src/org/sipdroid/sipua/SipdroidEngine.java (working copy)

@@ -208,10 +208,25 @@
} else
Receiver.onText(Receiver.REGISTER_NOTIFICATION, null, 0,0);
Receiver.registered();
+ if (PreferenceManager.getDefaultSharedPreferences(getUIContext()).getBoolean("MWI_enabled",true)) {
+ ra.startMWI();
+ }
if (wl.isHeld())


wl.release();
}

+ public void onMWIUpdate(boolean voicemail, int number) {
+ if (voicemail) {
+ String msgs = getUIContext().getString(R.string.voicemail);
+ if (number != 0) {
+ msgs = msgs + ": " + number;
+ }
+ Receiver.onText(Receiver.MWI_NOTIFICATION, msgs,android.R.drawable.stat_notify_voicemail,0);
+ } else {
+ Receiver.onText(Receiver.MWI_NOTIFICATION, null, 0,0);
+ }
+ }
+
static long lasthalt;

/** When a UA failed on (un)registering. */

@@ -225,6 +240,7 @@
sip_provider.haltConnections();
}
updateDNS();
+ ra.stopMWI();
}

public void updateDNS() {

@@ -103,6 +106,11 @@



UserAgentProfile user_profile;

+ SubscriberDialog sd;

+ boolean alreadySubscribed = false;


+ Message currentSubscribeMessage;
+ public final int SUBSCRIPTION_EXPIRES = 184000;
+
/**
* Creates a new RegisterAgent with authentication credentials (i.e.
* username, realm, and passwd).

@@ -223,9 +231,151 @@



/** Unregister with the registrar server */
public boolean unregister() {
+ stopMWI();
return register(0);
}

+ public void stopMWI()


+ {
+ if (sd != null) {
+ synchronized (sd) {
+ sd.notify();
+ }
+ }

+ sd = null;

+ public void startMWI()
+ {
+ if (alreadySubscribed) {
+ return;

+ /* Can get replays of the subscription notice, so ignore */
+ if (alreadySubscribed) {
+ return;
+ }
+ alreadySubscribed = true;


+ if (resp.hasExpiresHeader()) {
+ expires = resp.getExpiresHeader().getDeltaSeconds();
+ } else {
+ expires = SUBSCRIPTION_EXPIRES;
+ }
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ try {
+ synchronized (sd) {
+ sd.wait(expires*1000);
+ }

+ alreadySubscribed = false;


+ startMWI();
+ } catch(Exception E) {
+ }
+ }
+ });
+ t.start();
+ }
+
+ public void onDlgSubscriptionFailure(SubscriberDialog dialog, int code,
+ String reason, Message resp)
+ {

+ Message req = getSubscribeMessage(true);

+ if (handleAuthentication(code, resp, req)) {


+ sd.subscribe(req);
+ } else {
+ delayStartMWI();
+ }
+ }
+
+ public void onDlgSubscribeTimeout(SubscriberDialog dialog)
+ {
+ delayStartMWI();
+ }
+
+ public void onDlgSubscriptionTerminated(SubscriberDialog dialog)
+ {

+ alreadySubscribed = false;

@@ -324,7 +474,7 @@
}
}

- private boolean generateRequestWithProxyAuthorizationheader(TransactionClient transaction,
+ private boolean generateRequestWithProxyAuthorizationheader(
Message resp, Message req){
if(resp.hasProxyAuthenticateHeader()
&& resp.getProxyAuthenticateHeader().getRealmParam()
@@ -338,20 +488,17 @@


qop = (qop_options != null) ? "auth" : null;

ProxyAuthorizationHeader ah = (new DigestAuthentication(
- SipMethods.REGISTER, req.getRequestLine().getAddress()
+ req.getTransactionMethod(), req.getRequestLine().getAddress()
.toString(), pah, qop, null, username, passwd))
.getProxyAuthorizationHeader();
req.setProxyAuthorizationHeader(ah);

- TransactionClient t = new TransactionClient(sip_provider, req, this);
-
- t.request();
return true;
}
return false;
}

- private boolean generateRequestWithWwwAuthorizationheader(TransactionClient transaction,
+ private boolean generateRequestWithWwwAuthorizationheader(
Message resp, Message req){
if(resp.hasWwwAuthenticateHeader()
&& resp.getWwwAuthenticateHeader().getRealmParam()
@@ -365,18 +512,26 @@


qop = (qop_options != null) ? "auth" : null;

AuthorizationHeader ah = (new DigestAuthentication(

- SipMethods.REGISTER, req.getRequestLine().getAddress()
+ req.getTransactionMethod(), req.getRequestLine().getAddress()


.toString(), wah, qop, null, username, passwd))

.getAuthorizationHeader();
req.setAuthorizationHeader(ah);
-
- TransactionClient t = new TransactionClient(sip_provider, req, this);
-
- t.request();
return true;
}
return false;
}
+
+ private boolean handleAuthentication(int respCode, Message resp,
+ Message req) {
+ switch (respCode) {
+ case 407:
+ return generateRequestWithProxyAuthorizationheader(resp, req);
+ case 401:
+ return generateRequestWithWwwAuthorizationheader(resp, req);
+ }
+ return false;
+ }
+

private boolean processAuthenticationResponse(TransactionClient transaction,
Message resp, int respCode){
@@ -385,11 +540,11 @@
Message req = transaction.getRequestMessage();
req.setCSeqHeader(req.getCSeqHeader().incSequenceNumber());

- switch(respCode) {
- case 407:
- return generateRequestWithProxyAuthorizationheader(transaction, resp, req);
- case 401:
- return generateRequestWithWwwAuthorizationheader(transaction, resp, req);
+ if (handleAuthentication(respCode, resp, req)) {
+ TransactionClient t = new TransactionClient(sip_provider, req, this);
+
+ t.request();
+ return true;
}
}
return false;


Index: src/org/sipdroid/sipua/RegisterAgentListener.java
===================================================================
--- src/org/sipdroid/sipua/RegisterAgentListener.java (revision 329)
+++ src/org/sipdroid/sipua/RegisterAgentListener.java (working copy)
@@ -29,6 +29,8 @@
public void onUaRegistrationSuccess(RegisterAgent ra, NameAddress target,
NameAddress contact, String result);

+ public void onMWIUpdate(boolean voicemail, int number);
+
/** When a UA failed on (un)registering. */
public void onUaRegistrationFailure(RegisterAgent ra, NameAddress target,
NameAddress contact, String result);

Index: src/org/sipdroid/sipua/ui/Settings.java
===================================================================
--- src/org/sipdroid/sipua/ui/Settings.java (revision 329)
+++ src/org/sipdroid/sipua/ui/Settings.java (working copy)
@@ -60,6 +60,7 @@
Editor edit = getPreferenceScreen().getSharedPreferences().edit();

edit.putBoolean("wlan", true);
+ edit.putBoolean("MWI_enabled", true);
edit.putString("port", ""+SipStack.default_port);
edit.putString("server", "pbxes.org");
edit.putString("domain", "");
@@ -110,7 +111,8 @@
key.equals("protocol") ||
key.equals("minedge") ||
key.equals("pos") ||
- key.equals("posurl")) {
+ key.equals("posurl") ||
+ key.equals("MWI_enabled")) {
if (key.equals("wlan") || key.equals("3g"))
updateSleep();
Receiver.engine(this).halt();


Index: src/org/sipdroid/sipua/ui/Receiver.java
===================================================================
--- src/org/sipdroid/sipua/ui/Receiver.java (revision 329)
+++ src/org/sipdroid/sipua/ui/Receiver.java (working copy)
@@ -69,6 +69,7 @@
public final static int CALL_NOTIFICATION = 2;
public final static int MISSED_CALL_NOTIFICATION = 3;
public final static int AUTO_ANSWER_NOTIFICATION = 4;
+ public final static int MWI_NOTIFICATION = 5;

final static long[] vibratePattern = {0,1000,1000};

@@ -227,14 +228,18 @@
if (text != null) {
Notification notification = new Notification();
notification.icon = mInCallResId;
- if (type == MISSED_CALL_NOTIFICATION) {
+ if (type == MISSED_CALL_NOTIFICATION) {
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.setLatestEventInfo(mContext, text, mContext.getString(R.string.app_name),
PendingIntent.getActivity(mContext, 0, createCallLogIntent(), 0));
} else {
- notification.contentIntent = PendingIntent.getActivity(mContext, 0,
+ if (type == MWI_NOTIFICATION) {
+ notification.contentIntent = PendingIntent.getActivity(mContext, 0, createMWIIntent(), 0);
+ } else {
+ notification.contentIntent = PendingIntent.getActivity(mContext, 0,
createIntent(type == AUTO_ANSWER_NOTIFICATION?
AutoAnswer.class:Sipdroid.class), 0);
+ }
notification.flags |= Notification.FLAG_ONGOING_EVENT;
RemoteViews contentView = new RemoteViews(mContext.getPackageName(),
R.layout.ongoing_call_notification);
@@ -412,6 +417,12 @@
intent.addCategory(Intent.CATEGORY_HOME);
return intent;
}
+
+ static Intent createMWIIntent() {
+ String num = PreferenceManager.getDefaultSharedPreferences(mContext).getString("MWI_location", null);
+ Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("sip:" + num));
+ return intent;
+ }

public static void moveTop() {
onText(CALL_NOTIFICATION, mContext.getString(R.string.card_title_in_progress), R.drawable.stat_sys_phone_call, 0);


Index: res/values/strings.xml
===================================================================
--- res/values/strings.xml (revision 329)
+++ res/values/strings.xml (working copy)
@@ -51,6 +51,8 @@
<string name="regok">Registered</string>
<string name="regfailed">Registration failed</string>

+<string name="voicemail">Message(s) Waiting</string>
+
<string name="auto_enabled">Auto-Answer/Speakerphone</string>
<string name="auto_disabled">Auto-Answer off</string>
<string name="hint">Called Party Address</string>

@@ -91,6 +93,10 @@
<string name="settings_auto_on2">Short vibrating alert when screen is on</string>
<string name="settings_auto_ondemand">Auto-Answer on demand</string>
<string name="settings_auto_ondemand2">Show toggle switch in status bar</string>
+<string name="settings_MWI">Check for Voicemail</string>
+<string name="settings_MWI2">Subscribe to SIP message waiting</string>
+<string name="settings_MWI_number">Voicemail Location</string>
+<string name="settings_MWI_number2">SIP URL to call to pick up voicemail</string>

<string name="settings_advanced_options">Advanced Options</string>
<string name="settings_minedge">Signal Bars for EDGE Calls</string>
Index: res/xml/preferences.xml
===================================================================
--- res/xml/preferences.xml (revision 329)
+++ res/xml/preferences.xml (working copy)
@@ -57,6 +57,14 @@
android:key="auto_ondemand"
android:title="@string/settings_auto_ondemand"
android:summary="@string/settings_auto_ondemand2" />
+ <CheckBoxPreference
+ android:key="MWI_enabled"
+ android:title="@string/settings_MWI"
+ android:summary="@string/settings_MWI2" />
+ <EditTextPreference
+ android:key="MWI_location"
+ android:title="@string/settings_MWI_number"
+ android:summary="@string/settings_MWI_number2" />
</PreferenceCategory>
</PreferenceScreen>
<PreferenceScreen android:title="@string/settings_advanced_options">


Pascal Merle

unread,
Oct 7, 2009, 3:48:22 PM10/7/09
to Sipdroid Developers
What about checking this in?

I would prefer leaving the setting for MWI number out. This should be
retrieved from the server, from the MWI notification itself.

James Bottomley

unread,
Oct 7, 2009, 4:51:02 PM10/7/09
to sipdroid-...@googlegroups.com
On Wed, 2009-10-07 at 12:48 -0700, Pascal Merle wrote:
> What about checking this in?

Sure ... I'm not quite au fait with what goes on here ... do you just
apply patches straight from the mailing list to the tree, or does
something else have to happen?

> I would prefer leaving the setting for MWI number out. This should be
> retrieved from the server, from the MWI notification itself.

RFC3842 doesn't define how you get the message retrieval call number
(message account isn't the same thing as the uri to call). I'm sure
there are systems that can supply it as part of the notify extended
headers, but it would be a system specific extension, which is why for
the general case, you need to supply the number to call to get to
voicemail.

James


Pascal Merle

unread,
Oct 8, 2009, 4:14:56 AM10/8/09
to Sipdroid Developers
No, you please check them in into the svn repository.

Before committing, let's discuss getting the number from the message-
account field instead of a configurable option.

It seems some phones are really doing this like the Snom. And all
providers I have checked supply the right number in the message-
account field. What is your experience with the provider you are using?

James Bottomley

unread,
Oct 8, 2009, 9:20:46 AM10/8/09
to sipdroid-...@googlegroups.com
On Thu, 2009-10-08 at 01:14 -0700, Pascal Merle wrote:
> No, you please check them in into the svn repository.

OK, so that means I'll need commit rights. I assume someone needs an
ssh key?

> Before committing, let's discuss getting the number from the message-
> account field instead of a configurable option.

Sure ... but we still need a configurable field, even if it gets
pre-populated

> It seems some phones are really doing this like the Snom. And all
> providers I have checked supply the right number in the message-
> account field. What is your experience with the provider you are using?

OK, so asterisk, which is what I use just follows the RFCs, so no call
number. Where is this defined for the other systems? It's going to be
a simple body field ... I just need to know what it is.

James


Pascal Merle

unread,
Oct 8, 2009, 1:47:54 PM10/8/09
to Sipdroid Developers
No ssh key. Your google ID is added as a member. That's how you
authenticate.

Asterisk has an option "vmexten=". There you enter *97 (or any other
number to call). I would start with solely getting the number from the
NOTIFY messages. If this leads to any problems another option can be
added in a future release. I don't want to add too many options to the
device as this gets confusing and uneasy to manage.

James Bottomley

unread,
Oct 9, 2009, 4:55:54 PM10/9/09
to sipdroid-...@googlegroups.com

OK, so here's the update. It takes the voicemail number from the
Message-Account header ... it strips the realm because they're usually
wrong for private asterisk because of nat problems and then vectors to
it. If there's no account, it just dumps the user in a dialer.

James

---

Index: src/org/sipdroid/sipua/SipdroidEngine.java
===================================================================
--- src/org/sipdroid/sipua/SipdroidEngine.java (revision 330)
+++ src/org/sipdroid/sipua/SipdroidEngine.java (working copy)
@@ -216,10 +216,26 @@


} else
Receiver.onText(Receiver.REGISTER_NOTIFICATION, null, 0,0);
Receiver.registered();
+ if (PreferenceManager.getDefaultSharedPreferences(getUIContext()).getBoolean("MWI_enabled",true)) {
+ ra.startMWI();
+ }
if (wl.isHeld())
wl.release();
}

+ public void onMWIUpdate(boolean voicemail, int number, String vmacc) {


+ if (voicemail) {
+ String msgs = getUIContext().getString(R.string.voicemail);
+ if (number != 0) {
+ msgs = msgs + ": " + number;
+ }

+ Receiver.MWI_account = vmacc;


+ Receiver.onText(Receiver.MWI_NOTIFICATION, msgs,android.R.drawable.stat_notify_voicemail,0);
+ } else {
+ Receiver.onText(Receiver.MWI_NOTIFICATION, null, 0,0);
+ }
+ }
+
static long lasthalt;

/** When a UA failed on (un)registering. */

@@ -233,6 +249,7 @@


sip_provider.haltConnections();
}
updateDNS();
+ ra.stopMWI();
}

public void updateDNS() {
Index: src/org/sipdroid/sipua/RegisterAgent.java
===================================================================

--- src/org/sipdroid/sipua/RegisterAgent.java (revision 330)

@@ -223,9 +231,157 @@



/** Unregister with the registrar server */
public boolean unregister() {
+ stopMWI();
return register(0);
}

+ public void stopMWI()
+ {
+ if (sd != null) {
+ synchronized (sd) {
+ sd.notify();
+ }
+ }
+ sd = null;

+ listener.onMWIUpdate(false, 0, null);

+ final char[] vmboxsep = { '@', '\r', '\n' };
+ String vmaccount = null;


+ boolean voicemail = false;
+ int nummsg = 0;
+ while (p.hasMore()) {
+ String property = p.getWord(propertysep);
+ p.skipChar();
+ p.skipWSP();
+ String value = p.getWord(p.CRLF);
+ if (property.equalsIgnoreCase("Messages-Waiting") && value.equalsIgnoreCase("yes")) {
+ voicemail = true;
+ } else if (property.equalsIgnoreCase("Voice-Message")) {
+ Parser np = new Parser(value);
+ String num = np.getWord(vmailsep);
+ nummsg = Integer.parseInt(num);

+ } else if (property.equalsIgnoreCase("Message-Account")) {


+ Parser np = new Parser(value);

+ // strip the @<pbx> because it may have nat problems
+ vmaccount = np.getWord(vmboxsep);
+ }
+ }
+ listener.onMWIUpdate(voicemail, nummsg, vmaccount);


+ }
+
// **************** Transaction callback functions *****************

/** Callback function called when client sends back a failure response. */

@@ -324,7 +480,7 @@


}
}

- private boolean generateRequestWithProxyAuthorizationheader(TransactionClient transaction,
+ private boolean generateRequestWithProxyAuthorizationheader(
Message resp, Message req){
if(resp.hasProxyAuthenticateHeader()
&& resp.getProxyAuthenticateHeader().getRealmParam()

@@ -338,20 +494,17 @@


qop = (qop_options != null) ? "auth" : null;

ProxyAuthorizationHeader ah = (new DigestAuthentication(
- SipMethods.REGISTER, req.getRequestLine().getAddress()
+ req.getTransactionMethod(), req.getRequestLine().getAddress()
.toString(), pah, qop, null, username, passwd))
.getProxyAuthorizationHeader();
req.setProxyAuthorizationHeader(ah);

- TransactionClient t = new TransactionClient(sip_provider, req, this);
-
- t.request();
return true;
}
return false;
}

- private boolean generateRequestWithWwwAuthorizationheader(TransactionClient transaction,
+ private boolean generateRequestWithWwwAuthorizationheader(
Message resp, Message req){
if(resp.hasWwwAuthenticateHeader()
&& resp.getWwwAuthenticateHeader().getRealmParam()

@@ -365,18 +518,26 @@

@@ -385,11 +546,11 @@


Message req = transaction.getRequestMessage();
req.setCSeqHeader(req.getCSeqHeader().incSequenceNumber());

- switch(respCode) {
- case 407:
- return generateRequestWithProxyAuthorizationheader(transaction, resp, req);
- case 401:
- return generateRequestWithWwwAuthorizationheader(transaction, resp, req);
+ if (handleAuthentication(respCode, resp, req)) {
+ TransactionClient t = new TransactionClient(sip_provider, req, this);
+
+ t.request();
+ return true;
}
}
return false;
Index: src/org/sipdroid/sipua/RegisterAgentListener.java
===================================================================

--- src/org/sipdroid/sipua/RegisterAgentListener.java (revision 330)


+++ src/org/sipdroid/sipua/RegisterAgentListener.java (working copy)
@@ -29,6 +29,8 @@
public void onUaRegistrationSuccess(RegisterAgent ra, NameAddress target,
NameAddress contact, String result);

+ public void onMWIUpdate(boolean voicemail, int number, String vmacc);


+
/** When a UA failed on (un)registering. */
public void onUaRegistrationFailure(RegisterAgent ra, NameAddress target,
NameAddress contact, String result);
Index: src/org/sipdroid/sipua/ui/Settings.java
===================================================================

--- src/org/sipdroid/sipua/ui/Settings.java (revision 330)


+++ src/org/sipdroid/sipua/ui/Settings.java (working copy)
@@ -60,6 +60,7 @@
Editor edit = getPreferenceScreen().getSharedPreferences().edit();

edit.putBoolean("wlan", true);
+ edit.putBoolean("MWI_enabled", true);
edit.putString("port", ""+SipStack.default_port);
edit.putString("server", "pbxes.org");
edit.putString("domain", "");

@@ -111,7 +112,8 @@


key.equals("minedge") ||
key.equals("pos") ||

key.equals("posurl") ||
- key.equals("callerid")) {
+ key.equals("callerid") ||


+ key.equals("MWI_enabled")) {
if (key.equals("wlan") || key.equals("3g"))
updateSleep();
Receiver.engine(this).halt();
Index: src/org/sipdroid/sipua/ui/Receiver.java
===================================================================

--- src/org/sipdroid/sipua/ui/Receiver.java (revision 330)


+++ src/org/sipdroid/sipua/ui/Receiver.java (working copy)
@@ -69,6 +69,7 @@
public final static int CALL_NOTIFICATION = 2;
public final static int MISSED_CALL_NOTIFICATION = 3;
public final static int AUTO_ANSWER_NOTIFICATION = 4;
+ public final static int MWI_NOTIFICATION = 5;

final static long[] vibratePattern = {0,1000,1000};

@@ -82,6 +83,7 @@
public static int call_state;

public static String pstn_state;
+ public static String MWI_account;
private static String laststate,lastnumber;

public static MediaPlayer ringbackPlayer;
@@ -227,14 +229,18 @@


if (text != null) {
Notification notification = new Notification();
notification.icon = mInCallResId;
- if (type == MISSED_CALL_NOTIFICATION) {
+ if (type == MISSED_CALL_NOTIFICATION) {
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.setLatestEventInfo(mContext, text, mContext.getString(R.string.app_name),
PendingIntent.getActivity(mContext, 0, createCallLogIntent(), 0));
} else {
- notification.contentIntent = PendingIntent.getActivity(mContext, 0,
+ if (type == MWI_NOTIFICATION) {
+ notification.contentIntent = PendingIntent.getActivity(mContext, 0, createMWIIntent(), 0);
+ } else {
+ notification.contentIntent = PendingIntent.getActivity(mContext, 0,
createIntent(type == AUTO_ANSWER_NOTIFICATION?
AutoAnswer.class:Sipdroid.class), 0);
+ }
notification.flags |= Notification.FLAG_ONGOING_EVENT;
RemoteViews contentView = new RemoteViews(mContext.getPackageName(),
R.layout.ongoing_call_notification);

@@ -412,6 +418,16 @@


intent.addCategory(Intent.CATEGORY_HOME);
return intent;
}
+
+ static Intent createMWIIntent() {

+ Intent intent;
+
+ if (MWI_account != null)
+ intent = new Intent(Intent.ACTION_CALL, Uri.parse(MWI_account));
+ else
+ intent = new Intent(Intent.ACTION_DIAL);


+ return intent;
+ }

public static void moveTop() {
onText(CALL_NOTIFICATION, mContext.getString(R.string.card_title_in_progress), R.drawable.stat_sys_phone_call, 0);
Index: res/values/strings.xml
===================================================================

--- res/values/strings.xml (revision 330)


+++ res/values/strings.xml (working copy)
@@ -51,6 +51,8 @@
<string name="regok">Registered</string>
<string name="regfailed">Registration failed</string>

+<string name="voicemail">Message(s) Waiting</string>
+
<string name="auto_enabled">Auto-Answer/Speakerphone</string>
<string name="auto_disabled">Auto-Answer off</string>
<string name="hint">Called Party Address</string>

@@ -91,6 +93,8 @@


<string name="settings_auto_on2">Short vibrating alert when screen is on</string>
<string name="settings_auto_ondemand">Auto-Answer on demand</string>
<string name="settings_auto_ondemand2">Show toggle switch in status bar</string>
+<string name="settings_MWI">Check for Voicemail</string>
+<string name="settings_MWI2">Subscribe to SIP message waiting</string>

<string name="settings_advanced_options">Advanced Options</string>
<string name="settings_minedge">Signal Bars for EDGE Calls</string>
Index: res/xml/preferences.xml
===================================================================

--- res/xml/preferences.xml (revision 330)
+++ res/xml/preferences.xml (working copy)
@@ -56,6 +56,10 @@


android:key="auto_ondemand"
android:title="@string/settings_auto_ondemand"
android:summary="@string/settings_auto_ondemand2" />
+ <CheckBoxPreference
+ android:key="MWI_enabled"
+ android:title="@string/settings_MWI"
+ android:summary="@string/settings_MWI2" />

Reply all
Reply to author
Forward
0 new messages