Android SIP API : keep alive の動作について

SipProfile.getSendKeepAlive は、keep alive を行うかどうかを取得します。

keep alive について、リファレンスでは以下のようにあるだけです。

[SipProfile getSendKeepAlive](https://developer.android.com/reference/android/net/sip/SipProfile#getSendKeepAlive())
> the flag of sending SIP keep-alive messages



[SipProfile.Builder setSendKeepAlive](https://developer.android.com/reference/android/net/sip/SipProfile.Builder#setSendKeepAlive(boolean))
> boolean:true if sending keep-alive message is required, false otherwise


以下も、keep alive の記述はありません。

* [セッション開始プロトコルの概要](https://developer.android.com/guide/topics/connectivity/sip)
* [SipDemo (サンプルコード)](https://github.com/aosp-mirror/platform_development/tree/master/samples/SipDemo)


ので、android のソースコードから動作を調べることにしました。


# 参考にしたバージョン

android 7.0 のソースコードを参考にすることにしました。以下のサイトで調べました。

https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/android/net/sip/SipManager.java

https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;l=1040


# 結論

* REGISTER リクエストの成功後、定期的に OPTIONS リクエストを送信する。
* 送信間隔は、20 ～ 120 秒。
* OPTIONS リクエストの応答がない場合、OPTIONS レスポンスを確認し RPORT の変更があった場合、REGISTER リクエストを送信する。
* WIFI を使用していると判断される場合 (※1) は、SipProfile.getKeepAlive が false でもこの動作をする。

※1 ... SipService.isBehindNAT を参照。WIFI を使用しているかどうかは、IP アドレスにより判断している。


# ソースコードの調査


## SipProfile.getSendKeepAlive の呼び出し箇所

SipProfile.getSendKeepAlive メソッドを呼び出す個所は SipAutoReg.onRegistrationDone 。

https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;l=1040

SipService.isBehindNAT メソッドが true を返す、または、SipProfile.getSendKeepAlive メソッドが true を返す、場合、SipAutoReg.startKeepAliveProcess メソッドを実行。

以降の呼び出しは以下の通り。
SipAutoReg.onRegistrationDone
→ [SigAutoReg.startKeepAliveProcess](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;l=837;drc=android-7.0.0_r35)
→ [SipSessionImpl.startKeepAliveProcess](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipSessionGroup.java;l=1502;drc=android-7.0.0_r35)
→ [SipKeepAlive.start](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipSessionGroup.java;drc=android-7.0.0_r35;l=1537) 


## SipKeepAlive クラス start メソッド
[SipKeepAlive.start](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipSessionGroup.java;drc=android-7.0.0_r35;l=1537) 
引数 interval により、タイマ (mWakeupTimer) を開始。

[SipKeepAlive.run](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipSessionGroup.java;l=1583;drc=android-7.0.0_r35)
タイマにより、run メソッドが実行。SipKeepAlive.sendKeepAlive メソッドを実行。


[SipKeepAlive.sendKeepAlive](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipSessionGroup.java;drc=android-7.0.0_r35;l=1621)
SipHelper.sendOptions メソッドを実行。SipHelper.sendOptions は OPTIONS リクエストを送信している。
SipSessionImple.startSessionTimer メソッドでは、SessionTimer を作成。SessionTimer は start メソッドの引数 timeout が経過すると SipSessionImpl.onError を実行する。(応答のタイムアウト判定)


### SessionTimer クラス
onError を実行されないためには、[SessionTimer.cancel](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipSessionGroup.java;drc=android-7.0.0_r35;l=559) を実行する必要がある。


SipSessionTimer.cancel メソッドの呼び出し元 (keep alive に関連するものだけ) をたどると
SipSessionTimer.cancel
← SipSessionImpl.cancelSessionTimer
← SipKeepAlive.process 
← SipSessionImpl.process
← SipSessionGroup.process
← SipSessionGroup.processResponse … レスポンスの受信の際に呼ばれるはず


また、タイムアウト時の呼び出しは以下の通り。
SessionTimer.timeout
→ SipSessionImpl.onError
→ SipSessionImpl.endCallOnError
→ SipSessionListenerProxy.onError (SipSessionImpl.startKeepAlive で SipKeepAlive をセット)
→ SipKeepAlive.onError 
→ SipAutoReg.onError (SipKeepAlive.startKeepAliveProcess の引数 callback へ通知)




### SipKeepAlive.process

[SipKeepAlive.process](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipSessionGroup.java;l=1553;drc=android-7.0.0_r35;bpv=1;bpt=1) メソッドは、レスポンスを解析する。

RPORTが変更されている場合、SipKeepAlive.stop にて、mWakeupTimer を停止。SipSessionImpl.reset も呼ばれ、SipSessionImpl.cancelSessionTimer も呼ばれる。(Keep Alive 動作の停止)
RPORTが変更されていない場合、SipSessionImpl.cancelSessionTimer を呼ぶ。(応答タイムアウト判定のキャンセル)

KeepAliveProccessCallback.onResponse を呼び出し。 これは SipAutoReg が実装している。


## SipAutoReg.onResponse , SipAutoReg.onError 

[SipAutoReg.onResponse](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;l=861;drc=android-7.0.0_r35)
portChanged が true であれば、SipSessionImpl.register を実行。
SipSessionImpl.register は、REGISTER リクエストの送信を行う。

[SipAutoReg.onError](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;l=908;drc=android-7.0.0_r35)
SipAutoReg.onResponse(true) を実行。




## その他

### SipService.isBehindNAT

https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;l=451;drc=android-7.0.0_r35

引数に String 型の変数をとり、```10.*.*.*``` 、```172.16.*.*```、```192.168.*.*``` のいずれかに該当する場合、true を返す。

wifi を使用している場合は、大抵これらのアドレスに該当する。SipProfile.getSendKeepAlive によらず、keep alive が有効になる。


### keep alive の送信間隔の変更

送信間隔の変更は、SipKeepAliveProcessCallback で行っている。
このクラスも OPTIONS リクエストを送信している。
(このあたりよくわからないが、SipServiceで保持されるインスタンスは1つだけなのでいいのかな)

https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;l=629;drc=android-7.0.0_r35;bpv=0;bpt=1


送信間隔変更時は以下のような呼び出しになる。

[SipKeepAliveProcessCallback.onResponse](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;l=721;drc=android-7.0.0_r35;bpv=0;bpt=1)
→ [SipService.onKeepAliveIntervalChanged()](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;l=441;drc=android-7.0.0_r35
)
→ [SipSessionGroupExt.onKeepAliveIntervalChanged](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;drc=android-7.0.0_r35;l=507)
→ [SipAutoReg.onKeepAliveIntervalChanged](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;drc=android-7.0.0_r35;l=933)
→ [SipAutoReg.startKeepAliveProcess](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;drc=android-7.0.0_r35;l=839)


# TODO

keep alive の動作は大体わかったので、SipAutoReg の使用箇所を調べる。


## SipAutoReg の使用箇所

SipAutoReg クラスは、[SipSessionGroupExt](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;l=484;drc=android-7.0.0_r35;bpv=0;bpt=0) クラスで使用されている。

SipSessionGroupExt クラスは、[SipService.createGroup](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;l=321;drc=android-7.0.0_r35;bpv=0;bpt=0) , [SipService.createGroup](https://cs.android.com/android/platform/superproject/+/android-7.0.0_r35:frameworks/opt/net/voip/src/java/com/android/server/sip/SipService.java;l=307;drc=android-7.0.0_r35;bpv=0;bpt=0) で作成される。

これらは、SipService.createSession , SipService.open , SipService.open3 で使用される。