summaryrefslogtreecommitdiff
path: root/voip/java/com/android/server/sip/SipService.java
diff options
context:
space:
mode:
authorWink Saville <wink@google.com>2013-01-28 15:27:47 -0800
committerWink Saville <wink@google.com>2013-01-28 15:27:47 -0800
commitcbb2a2a207796a68eff26be001af2797594d3b3c (patch)
treeae7d166136064ba408035fa741bb944441fddb47 /voip/java/com/android/server/sip/SipService.java
parent29e4aa7abe6b0c62f31fb23b8572cd5b91f08f5c (diff)
Delete frameworks/base/voip use voip-common from frameworks/opt/net/voip
Change-Id: Ieaba759a0f69b45c4b8839cbed1fe757cdf190c5
Diffstat (limited to 'voip/java/com/android/server/sip/SipService.java')
-rw-r--r--voip/java/com/android/server/sip/SipService.java1262
1 files changed, 0 insertions, 1262 deletions
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
deleted file mode 100644
index 80fe68c384c7..000000000000
--- a/voip/java/com/android/server/sip/SipService.java
+++ /dev/null
@@ -1,1262 +0,0 @@
-/*
- * Copyright (C) 2010, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.sip;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.sip.ISipService;
-import android.net.sip.ISipSession;
-import android.net.sip.ISipSessionListener;
-import android.net.sip.SipErrorCode;
-import android.net.sip.SipManager;
-import android.net.sip.SipProfile;
-import android.net.sip.SipSession;
-import android.net.sip.SipSessionAdapter;
-import android.net.wifi.WifiManager;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.telephony.Rlog;
-
-import java.io.IOException;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Executor;
-import javax.sip.SipException;
-
-/**
- * @hide
- */
-public final class SipService extends ISipService.Stub {
- static final String TAG = "SipService";
- static final boolean DBG = true;
- private static final int EXPIRY_TIME = 3600;
- private static final int SHORT_EXPIRY_TIME = 10;
- private static final int MIN_EXPIRY_TIME = 60;
- private static final int DEFAULT_KEEPALIVE_INTERVAL = 10; // in seconds
- private static final int DEFAULT_MAX_KEEPALIVE_INTERVAL = 120; // in seconds
-
- private Context mContext;
- private String mLocalIp;
- private int mNetworkType = -1;
- private SipWakeupTimer mTimer;
- private WifiManager.WifiLock mWifiLock;
- private boolean mSipOnWifiOnly;
-
- private SipKeepAliveProcessCallback mSipKeepAliveProcessCallback;
-
- private MyExecutor mExecutor = new MyExecutor();
-
- // SipProfile URI --> group
- private Map<String, SipSessionGroupExt> mSipGroups =
- new HashMap<String, SipSessionGroupExt>();
-
- // session ID --> session
- private Map<String, ISipSession> mPendingSessions =
- new HashMap<String, ISipSession>();
-
- private ConnectivityReceiver mConnectivityReceiver;
- private SipWakeLock mMyWakeLock;
- private int mKeepAliveInterval;
- private int mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
-
- /**
- * Starts the SIP service. Do nothing if the SIP API is not supported on the
- * device.
- */
- public static void start(Context context) {
- if (SipManager.isApiSupported(context)) {
- ServiceManager.addService("sip", new SipService(context));
- context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
- if (DBG) slog("start:");
- }
- }
-
- private SipService(Context context) {
- if (DBG) log("SipService: started!");
- mContext = context;
- mConnectivityReceiver = new ConnectivityReceiver();
-
- mWifiLock = ((WifiManager)
- context.getSystemService(Context.WIFI_SERVICE))
- .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
- mWifiLock.setReferenceCounted(false);
- mSipOnWifiOnly = SipManager.isSipWifiOnly(context);
-
- mMyWakeLock = new SipWakeLock((PowerManager)
- context.getSystemService(Context.POWER_SERVICE));
-
- mTimer = new SipWakeupTimer(context, mExecutor);
- }
-
- @Override
- public synchronized SipProfile[] getListOfProfiles() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- boolean isCallerRadio = isCallerRadio();
- ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
- for (SipSessionGroupExt group : mSipGroups.values()) {
- if (isCallerRadio || isCallerCreator(group)) {
- profiles.add(group.getLocalProfile());
- }
- }
- return profiles.toArray(new SipProfile[profiles.size()]);
- }
-
- @Override
- public synchronized void open(SipProfile localProfile) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- localProfile.setCallingUid(Binder.getCallingUid());
- try {
- createGroup(localProfile);
- } catch (SipException e) {
- loge("openToMakeCalls()", e);
- // TODO: how to send the exception back
- }
- }
-
- @Override
- public synchronized void open3(SipProfile localProfile,
- PendingIntent incomingCallPendingIntent,
- ISipSessionListener listener) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- localProfile.setCallingUid(Binder.getCallingUid());
- if (incomingCallPendingIntent == null) {
- if (DBG) log("open3: incomingCallPendingIntent cannot be null; "
- + "the profile is not opened");
- return;
- }
- if (DBG) log("open3: " + localProfile.getUriString() + ": "
- + incomingCallPendingIntent + ": " + listener);
- try {
- SipSessionGroupExt group = createGroup(localProfile,
- incomingCallPendingIntent, listener);
- if (localProfile.getAutoRegistration()) {
- group.openToReceiveCalls();
- updateWakeLocks();
- }
- } catch (SipException e) {
- loge("open3:", e);
- // TODO: how to send the exception back
- }
- }
-
- private boolean isCallerCreator(SipSessionGroupExt group) {
- SipProfile profile = group.getLocalProfile();
- return (profile.getCallingUid() == Binder.getCallingUid());
- }
-
- private boolean isCallerCreatorOrRadio(SipSessionGroupExt group) {
- return (isCallerRadio() || isCallerCreator(group));
- }
-
- private boolean isCallerRadio() {
- return (Binder.getCallingUid() == Process.PHONE_UID);
- }
-
- @Override
- public synchronized void close(String localProfileUri) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group == null) return;
- if (!isCallerCreatorOrRadio(group)) {
- if (DBG) log("only creator or radio can close this profile");
- return;
- }
-
- group = mSipGroups.remove(localProfileUri);
- notifyProfileRemoved(group.getLocalProfile());
- group.close();
-
- updateWakeLocks();
- }
-
- @Override
- public synchronized boolean isOpened(String localProfileUri) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group == null) return false;
- if (isCallerCreatorOrRadio(group)) {
- return true;
- } else {
- if (DBG) log("only creator or radio can query on the profile");
- return false;
- }
- }
-
- @Override
- public synchronized boolean isRegistered(String localProfileUri) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group == null) return false;
- if (isCallerCreatorOrRadio(group)) {
- return group.isRegistered();
- } else {
- if (DBG) log("only creator or radio can query on the profile");
- return false;
- }
- }
-
- @Override
- public synchronized void setRegistrationListener(String localProfileUri,
- ISipSessionListener listener) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group == null) return;
- if (isCallerCreator(group)) {
- group.setListener(listener);
- } else {
- if (DBG) log("only creator can set listener on the profile");
- }
- }
-
- @Override
- public synchronized ISipSession createSession(SipProfile localProfile,
- ISipSessionListener listener) {
- if (DBG) log("createSession: profile" + localProfile);
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- localProfile.setCallingUid(Binder.getCallingUid());
- if (mNetworkType == -1) {
- if (DBG) log("createSession: mNetworkType==-1 ret=null");
- return null;
- }
- try {
- SipSessionGroupExt group = createGroup(localProfile);
- return group.createSession(listener);
- } catch (SipException e) {
- if (DBG) loge("createSession;", e);
- return null;
- }
- }
-
- @Override
- public synchronized ISipSession getPendingSession(String callId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- if (callId == null) return null;
- return mPendingSessions.get(callId);
- }
-
- private String determineLocalIp() {
- try {
- DatagramSocket s = new DatagramSocket();
- s.connect(InetAddress.getByName("192.168.1.1"), 80);
- return s.getLocalAddress().getHostAddress();
- } catch (IOException e) {
- if (DBG) loge("determineLocalIp()", e);
- // dont do anything; there should be a connectivity change going
- return null;
- }
- }
-
- private SipSessionGroupExt createGroup(SipProfile localProfile)
- throws SipException {
- String key = localProfile.getUriString();
- SipSessionGroupExt group = mSipGroups.get(key);
- if (group == null) {
- group = new SipSessionGroupExt(localProfile, null, null);
- mSipGroups.put(key, group);
- notifyProfileAdded(localProfile);
- } else if (!isCallerCreator(group)) {
- throw new SipException("only creator can access the profile");
- }
- return group;
- }
-
- private SipSessionGroupExt createGroup(SipProfile localProfile,
- PendingIntent incomingCallPendingIntent,
- ISipSessionListener listener) throws SipException {
- String key = localProfile.getUriString();
- SipSessionGroupExt group = mSipGroups.get(key);
- if (group != null) {
- if (!isCallerCreator(group)) {
- throw new SipException("only creator can access the profile");
- }
- group.setIncomingCallPendingIntent(incomingCallPendingIntent);
- group.setListener(listener);
- } else {
- group = new SipSessionGroupExt(localProfile,
- incomingCallPendingIntent, listener);
- mSipGroups.put(key, group);
- notifyProfileAdded(localProfile);
- }
- return group;
- }
-
- private void notifyProfileAdded(SipProfile localProfile) {
- if (DBG) log("notify: profile added: " + localProfile);
- Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
- intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
- mContext.sendBroadcast(intent);
- if (mSipGroups.size() == 1) {
- registerReceivers();
- }
- }
-
- private void notifyProfileRemoved(SipProfile localProfile) {
- if (DBG) log("notify: profile removed: " + localProfile);
- Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
- intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
- mContext.sendBroadcast(intent);
- if (mSipGroups.size() == 0) {
- unregisterReceivers();
- }
- }
-
- private void stopPortMappingMeasurement() {
- if (mSipKeepAliveProcessCallback != null) {
- mSipKeepAliveProcessCallback.stop();
- mSipKeepAliveProcessCallback = null;
- }
- }
-
- private void startPortMappingLifetimeMeasurement(
- SipProfile localProfile) {
- startPortMappingLifetimeMeasurement(localProfile,
- DEFAULT_MAX_KEEPALIVE_INTERVAL);
- }
-
- private void startPortMappingLifetimeMeasurement(
- SipProfile localProfile, int maxInterval) {
- if ((mSipKeepAliveProcessCallback == null)
- && (mKeepAliveInterval == -1)
- && isBehindNAT(mLocalIp)) {
- if (DBG) log("startPortMappingLifetimeMeasurement: profile="
- + localProfile.getUriString());
-
- int minInterval = mLastGoodKeepAliveInterval;
- if (minInterval >= maxInterval) {
- // If mLastGoodKeepAliveInterval also does not work, reset it
- // to the default min
- minInterval = mLastGoodKeepAliveInterval
- = DEFAULT_KEEPALIVE_INTERVAL;
- log(" reset min interval to " + minInterval);
- }
- mSipKeepAliveProcessCallback = new SipKeepAliveProcessCallback(
- localProfile, minInterval, maxInterval);
- mSipKeepAliveProcessCallback.start();
- }
- }
-
- private void restartPortMappingLifetimeMeasurement(
- SipProfile localProfile, int maxInterval) {
- stopPortMappingMeasurement();
- mKeepAliveInterval = -1;
- startPortMappingLifetimeMeasurement(localProfile, maxInterval);
- }
-
- private synchronized void addPendingSession(ISipSession session) {
- try {
- cleanUpPendingSessions();
- mPendingSessions.put(session.getCallId(), session);
- if (DBG) log("#pending sess=" + mPendingSessions.size());
- } catch (RemoteException e) {
- // should not happen with a local call
- loge("addPendingSession()", e);
- }
- }
-
- private void cleanUpPendingSessions() throws RemoteException {
- Map.Entry<String, ISipSession>[] entries =
- mPendingSessions.entrySet().toArray(
- new Map.Entry[mPendingSessions.size()]);
- for (Map.Entry<String, ISipSession> entry : entries) {
- if (entry.getValue().getState() != SipSession.State.INCOMING_CALL) {
- mPendingSessions.remove(entry.getKey());
- }
- }
- }
-
- private synchronized boolean callingSelf(SipSessionGroupExt ringingGroup,
- SipSessionGroup.SipSessionImpl ringingSession) {
- String callId = ringingSession.getCallId();
- for (SipSessionGroupExt group : mSipGroups.values()) {
- if ((group != ringingGroup) && group.containsSession(callId)) {
- if (DBG) log("call self: "
- + ringingSession.getLocalProfile().getUriString()
- + " -> " + group.getLocalProfile().getUriString());
- return true;
- }
- }
- return false;
- }
-
- private synchronized void onKeepAliveIntervalChanged() {
- for (SipSessionGroupExt group : mSipGroups.values()) {
- group.onKeepAliveIntervalChanged();
- }
- }
-
- private int getKeepAliveInterval() {
- return (mKeepAliveInterval < 0)
- ? mLastGoodKeepAliveInterval
- : mKeepAliveInterval;
- }
-
- private boolean isBehindNAT(String address) {
- try {
- // TODO: How is isBehindNAT used and why these constanst address:
- // 10.x.x.x | 192.168.x.x | 172.16.x.x .. 172.19.x.x
- byte[] d = InetAddress.getByName(address).getAddress();
- if ((d[0] == 10) ||
- (((0x000000FF & d[0]) == 172) &&
- ((0x000000F0 & d[1]) == 16)) ||
- (((0x000000FF & d[0]) == 192) &&
- ((0x000000FF & d[1]) == 168))) {
- return true;
- }
- } catch (UnknownHostException e) {
- loge("isBehindAT()" + address, e);
- }
- return false;
- }
-
- private class SipSessionGroupExt extends SipSessionAdapter {
- private static final String SSGE_TAG = "SipSessionGroupExt";
- private static final boolean SSGE_DBG = true;
- private SipSessionGroup mSipGroup;
- private PendingIntent mIncomingCallPendingIntent;
- private boolean mOpenedToReceiveCalls;
-
- private SipAutoReg mAutoRegistration =
- new SipAutoReg();
-
- public SipSessionGroupExt(SipProfile localProfile,
- PendingIntent incomingCallPendingIntent,
- ISipSessionListener listener) throws SipException {
- if (SSGE_DBG) log("SipSessionGroupExt: profile=" + localProfile);
- mSipGroup = new SipSessionGroup(duplicate(localProfile),
- localProfile.getPassword(), mTimer, mMyWakeLock);
- mIncomingCallPendingIntent = incomingCallPendingIntent;
- mAutoRegistration.setListener(listener);
- }
-
- public SipProfile getLocalProfile() {
- return mSipGroup.getLocalProfile();
- }
-
- public boolean containsSession(String callId) {
- return mSipGroup.containsSession(callId);
- }
-
- public void onKeepAliveIntervalChanged() {
- mAutoRegistration.onKeepAliveIntervalChanged();
- }
-
- // TODO: remove this method once SipWakeupTimer can better handle variety
- // of timeout values
- void setWakeupTimer(SipWakeupTimer timer) {
- mSipGroup.setWakeupTimer(timer);
- }
-
- private SipProfile duplicate(SipProfile p) {
- try {
- return new SipProfile.Builder(p).setPassword("*").build();
- } catch (Exception e) {
- loge("duplicate()", e);
- throw new RuntimeException("duplicate profile", e);
- }
- }
-
- public void setListener(ISipSessionListener listener) {
- mAutoRegistration.setListener(listener);
- }
-
- public void setIncomingCallPendingIntent(PendingIntent pIntent) {
- mIncomingCallPendingIntent = pIntent;
- }
-
- public void openToReceiveCalls() throws SipException {
- mOpenedToReceiveCalls = true;
- if (mNetworkType != -1) {
- mSipGroup.openToReceiveCalls(this);
- mAutoRegistration.start(mSipGroup);
- }
- if (SSGE_DBG) log("openToReceiveCalls: " + getUri() + ": "
- + mIncomingCallPendingIntent);
- }
-
- public void onConnectivityChanged(boolean connected)
- throws SipException {
- if (SSGE_DBG) {
- log("onConnectivityChanged: connected=" + connected + " uri="
- + getUri() + ": " + mIncomingCallPendingIntent);
- }
- mSipGroup.onConnectivityChanged();
- if (connected) {
- mSipGroup.reset();
- if (mOpenedToReceiveCalls) openToReceiveCalls();
- } else {
- mSipGroup.close();
- mAutoRegistration.stop();
- }
- }
-
- public void close() {
- mOpenedToReceiveCalls = false;
- mSipGroup.close();
- mAutoRegistration.stop();
- if (SSGE_DBG) log("close: " + getUri() + ": " + mIncomingCallPendingIntent);
- }
-
- public ISipSession createSession(ISipSessionListener listener) {
- if (SSGE_DBG) log("createSession");
- return mSipGroup.createSession(listener);
- }
-
- @Override
- public void onRinging(ISipSession s, SipProfile caller,
- String sessionDescription) {
- SipSessionGroup.SipSessionImpl session =
- (SipSessionGroup.SipSessionImpl) s;
- synchronized (SipService.this) {
- try {
- if (!isRegistered() || callingSelf(this, session)) {
- if (SSGE_DBG) log("onRinging: end notReg or self");
- session.endCall();
- return;
- }
-
- // send out incoming call broadcast
- addPendingSession(session);
- Intent intent = SipManager.createIncomingCallBroadcast(
- session.getCallId(), sessionDescription);
- if (SSGE_DBG) log("onRinging: uri=" + getUri() + ": "
- + caller.getUri() + ": " + session.getCallId()
- + " " + mIncomingCallPendingIntent);
- mIncomingCallPendingIntent.send(mContext,
- SipManager.INCOMING_CALL_RESULT_CODE, intent);
- } catch (PendingIntent.CanceledException e) {
- loge("onRinging: pendingIntent is canceled, drop incoming call", e);
- session.endCall();
- }
- }
- }
-
- @Override
- public void onError(ISipSession session, int errorCode,
- String message) {
- if (SSGE_DBG) log("onError: errorCode=" + errorCode + " desc="
- + SipErrorCode.toString(errorCode) + ": " + message);
- }
-
- public boolean isOpenedToReceiveCalls() {
- return mOpenedToReceiveCalls;
- }
-
- public boolean isRegistered() {
- return mAutoRegistration.isRegistered();
- }
-
- private String getUri() {
- return mSipGroup.getLocalProfileUri();
- }
-
- private void log(String s) {
- Rlog.d(SSGE_TAG, s);
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(SSGE_TAG, s, t);
- }
-
- }
-
- private class SipKeepAliveProcessCallback implements Runnable,
- SipSessionGroup.KeepAliveProcessCallback {
- private static final String SKAI_TAG = "SipKeepAliveProcessCallback";
- private static final boolean SKAI_DBG = true;
- private static final int MIN_INTERVAL = 5; // in seconds
- private static final int PASS_THRESHOLD = 10;
- private static final int NAT_MEASUREMENT_RETRY_INTERVAL = 120; // in seconds
- private SipProfile mLocalProfile;
- private SipSessionGroupExt mGroup;
- private SipSessionGroup.SipSessionImpl mSession;
- private int mMinInterval;
- private int mMaxInterval;
- private int mInterval;
- private int mPassCount;
-
- public SipKeepAliveProcessCallback(SipProfile localProfile,
- int minInterval, int maxInterval) {
- mMaxInterval = maxInterval;
- mMinInterval = minInterval;
- mLocalProfile = localProfile;
- }
-
- public void start() {
- synchronized (SipService.this) {
- if (mSession != null) {
- return;
- }
-
- mInterval = (mMaxInterval + mMinInterval) / 2;
- mPassCount = 0;
-
- // Don't start measurement if the interval is too small
- if (mInterval < DEFAULT_KEEPALIVE_INTERVAL || checkTermination()) {
- if (SKAI_DBG) log("start: measurement aborted; interval=[" +
- mMinInterval + "," + mMaxInterval + "]");
- return;
- }
-
- try {
- if (SKAI_DBG) log("start: interval=" + mInterval);
-
- mGroup = new SipSessionGroupExt(mLocalProfile, null, null);
- // TODO: remove this line once SipWakeupTimer can better handle
- // variety of timeout values
- mGroup.setWakeupTimer(new SipWakeupTimer(mContext, mExecutor));
-
- mSession = (SipSessionGroup.SipSessionImpl)
- mGroup.createSession(null);
- mSession.startKeepAliveProcess(mInterval, this);
- } catch (Throwable t) {
- onError(SipErrorCode.CLIENT_ERROR, t.toString());
- }
- }
- }
-
- public void stop() {
- synchronized (SipService.this) {
- if (mSession != null) {
- mSession.stopKeepAliveProcess();
- mSession = null;
- }
- if (mGroup != null) {
- mGroup.close();
- mGroup = null;
- }
- mTimer.cancel(this);
- if (SKAI_DBG) log("stop");
- }
- }
-
- private void restart() {
- synchronized (SipService.this) {
- // Return immediately if the measurement process is stopped
- if (mSession == null) return;
-
- if (SKAI_DBG) log("restart: interval=" + mInterval);
- try {
- mSession.stopKeepAliveProcess();
- mPassCount = 0;
- mSession.startKeepAliveProcess(mInterval, this);
- } catch (SipException e) {
- loge("restart", e);
- }
- }
- }
-
- private boolean checkTermination() {
- return ((mMaxInterval - mMinInterval) < MIN_INTERVAL);
- }
-
- // SipSessionGroup.KeepAliveProcessCallback
- @Override
- public void onResponse(boolean portChanged) {
- synchronized (SipService.this) {
- if (!portChanged) {
- if (++mPassCount != PASS_THRESHOLD) return;
- // update the interval, since the current interval is good to
- // keep the port mapping.
- if (mKeepAliveInterval > 0) {
- mLastGoodKeepAliveInterval = mKeepAliveInterval;
- }
- mKeepAliveInterval = mMinInterval = mInterval;
- if (SKAI_DBG) {
- log("onResponse: portChanged=" + portChanged + " mKeepAliveInterval="
- + mKeepAliveInterval);
- }
- onKeepAliveIntervalChanged();
- } else {
- // Since the rport is changed, shorten the interval.
- mMaxInterval = mInterval;
- }
- if (checkTermination()) {
- // update mKeepAliveInterval and stop measurement.
- stop();
- // If all the measurements failed, we still set it to
- // mMinInterval; If mMinInterval still doesn't work, a new
- // measurement with min interval=DEFAULT_KEEPALIVE_INTERVAL
- // will be conducted.
- mKeepAliveInterval = mMinInterval;
- if (SKAI_DBG) {
- log("onResponse: checkTermination mKeepAliveInterval="
- + mKeepAliveInterval);
- }
- } else {
- // calculate the new interval and continue.
- mInterval = (mMaxInterval + mMinInterval) / 2;
- if (SKAI_DBG) {
- log("onResponse: mKeepAliveInterval=" + mKeepAliveInterval
- + ", new mInterval=" + mInterval);
- }
- restart();
- }
- }
- }
-
- // SipSessionGroup.KeepAliveProcessCallback
- @Override
- public void onError(int errorCode, String description) {
- if (SKAI_DBG) loge("onError: errorCode=" + errorCode + " desc=" + description);
- restartLater();
- }
-
- // timeout handler
- @Override
- public void run() {
- mTimer.cancel(this);
- restart();
- }
-
- private void restartLater() {
- synchronized (SipService.this) {
- int interval = NAT_MEASUREMENT_RETRY_INTERVAL;
- mTimer.cancel(this);
- mTimer.set(interval * 1000, this);
- }
- }
-
- private void log(String s) {
- Rlog.d(SKAI_TAG, s);
- }
-
- private void loge(String s) {
- Rlog.d(SKAI_TAG, s);
- }
-
- private void loge(String s, Throwable t) {
- Rlog.d(SKAI_TAG, s, t);
- }
- }
-
- private class SipAutoReg extends SipSessionAdapter
- implements Runnable, SipSessionGroup.KeepAliveProcessCallback {
- private String SAR_TAG;
- private static final boolean SAR_DBG = true;
- private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10;
-
- private SipSessionGroup.SipSessionImpl mSession;
- private SipSessionGroup.SipSessionImpl mKeepAliveSession;
- private SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
- private int mBackoff = 1;
- private boolean mRegistered;
- private long mExpiryTime;
- private int mErrorCode;
- private String mErrorMessage;
- private boolean mRunning = false;
-
- private int mKeepAliveSuccessCount = 0;
-
- public void start(SipSessionGroup group) {
- if (!mRunning) {
- mRunning = true;
- mBackoff = 1;
- mSession = (SipSessionGroup.SipSessionImpl)
- group.createSession(this);
- // return right away if no active network connection.
- if (mSession == null) return;
-
- // start unregistration to clear up old registration at server
- // TODO: when rfc5626 is deployed, use reg-id and sip.instance
- // in registration to avoid adding duplicate entries to server
- mMyWakeLock.acquire(mSession);
- mSession.unregister();
- SAR_TAG = "SipAutoReg:" + mSession.getLocalProfile().getUriString();
- if (SAR_DBG) log("start: group=" + group);
- }
- }
-
- private void startKeepAliveProcess(int interval) {
- if (SAR_DBG) log("startKeepAliveProcess: interval=" + interval);
- if (mKeepAliveSession == null) {
- mKeepAliveSession = mSession.duplicate();
- } else {
- mKeepAliveSession.stopKeepAliveProcess();
- }
- try {
- mKeepAliveSession.startKeepAliveProcess(interval, this);
- } catch (SipException e) {
- loge("startKeepAliveProcess: interval=" + interval, e);
- }
- }
-
- private void stopKeepAliveProcess() {
- if (mKeepAliveSession != null) {
- mKeepAliveSession.stopKeepAliveProcess();
- mKeepAliveSession = null;
- }
- mKeepAliveSuccessCount = 0;
- }
-
- // SipSessionGroup.KeepAliveProcessCallback
- @Override
- public void onResponse(boolean portChanged) {
- synchronized (SipService.this) {
- if (portChanged) {
- int interval = getKeepAliveInterval();
- if (mKeepAliveSuccessCount < MIN_KEEPALIVE_SUCCESS_COUNT) {
- if (SAR_DBG) {
- log("onResponse: keepalive doesn't work with interval "
- + interval + ", past success count="
- + mKeepAliveSuccessCount);
- }
- if (interval > DEFAULT_KEEPALIVE_INTERVAL) {
- restartPortMappingLifetimeMeasurement(
- mSession.getLocalProfile(), interval);
- mKeepAliveSuccessCount = 0;
- }
- } else {
- if (SAR_DBG) {
- log("keep keepalive going with interval "
- + interval + ", past success count="
- + mKeepAliveSuccessCount);
- }
- mKeepAliveSuccessCount /= 2;
- }
- } else {
- // Start keep-alive interval measurement on the first
- // successfully kept-alive SipSessionGroup
- startPortMappingLifetimeMeasurement(
- mSession.getLocalProfile());
- mKeepAliveSuccessCount++;
- }
-
- if (!mRunning || !portChanged) return;
-
- // The keep alive process is stopped when port is changed;
- // Nullify the session so that the process can be restarted
- // again when the re-registration is done
- mKeepAliveSession = null;
-
- // Acquire wake lock for the registration process. The
- // lock will be released when registration is complete.
- mMyWakeLock.acquire(mSession);
- mSession.register(EXPIRY_TIME);
- }
- }
-
- // SipSessionGroup.KeepAliveProcessCallback
- @Override
- public void onError(int errorCode, String description) {
- if (SAR_DBG) {
- loge("onError: errorCode=" + errorCode + " desc=" + description);
- }
- onResponse(true); // re-register immediately
- }
-
- public void stop() {
- if (!mRunning) return;
- mRunning = false;
- mMyWakeLock.release(mSession);
- if (mSession != null) {
- mSession.setListener(null);
- if (mNetworkType != -1 && mRegistered) mSession.unregister();
- }
-
- mTimer.cancel(this);
- stopKeepAliveProcess();
-
- mRegistered = false;
- setListener(mProxy.getListener());
- }
-
- public void onKeepAliveIntervalChanged() {
- if (mKeepAliveSession != null) {
- int newInterval = getKeepAliveInterval();
- if (SAR_DBG) {
- log("onKeepAliveIntervalChanged: interval=" + newInterval);
- }
- mKeepAliveSuccessCount = 0;
- startKeepAliveProcess(newInterval);
- }
- }
-
- public void setListener(ISipSessionListener listener) {
- synchronized (SipService.this) {
- mProxy.setListener(listener);
-
- try {
- int state = (mSession == null)
- ? SipSession.State.READY_TO_CALL
- : mSession.getState();
- if ((state == SipSession.State.REGISTERING)
- || (state == SipSession.State.DEREGISTERING)) {
- mProxy.onRegistering(mSession);
- } else if (mRegistered) {
- int duration = (int)
- (mExpiryTime - SystemClock.elapsedRealtime());
- mProxy.onRegistrationDone(mSession, duration);
- } else if (mErrorCode != SipErrorCode.NO_ERROR) {
- if (mErrorCode == SipErrorCode.TIME_OUT) {
- mProxy.onRegistrationTimeout(mSession);
- } else {
- mProxy.onRegistrationFailed(mSession, mErrorCode,
- mErrorMessage);
- }
- } else if (mNetworkType == -1) {
- mProxy.onRegistrationFailed(mSession,
- SipErrorCode.DATA_CONNECTION_LOST,
- "no data connection");
- } else if (!mRunning) {
- mProxy.onRegistrationFailed(mSession,
- SipErrorCode.CLIENT_ERROR,
- "registration not running");
- } else {
- mProxy.onRegistrationFailed(mSession,
- SipErrorCode.IN_PROGRESS,
- String.valueOf(state));
- }
- } catch (Throwable t) {
- loge("setListener: ", t);
- }
- }
- }
-
- public boolean isRegistered() {
- return mRegistered;
- }
-
- // timeout handler: re-register
- @Override
- public void run() {
- synchronized (SipService.this) {
- if (!mRunning) return;
-
- mErrorCode = SipErrorCode.NO_ERROR;
- mErrorMessage = null;
- if (SAR_DBG) log("run: registering");
- if (mNetworkType != -1) {
- mMyWakeLock.acquire(mSession);
- mSession.register(EXPIRY_TIME);
- }
- }
- }
-
- private void restart(int duration) {
- if (SAR_DBG) log("restart: duration=" + duration + "s later.");
- mTimer.cancel(this);
- mTimer.set(duration * 1000, this);
- }
-
- private int backoffDuration() {
- int duration = SHORT_EXPIRY_TIME * mBackoff;
- if (duration > 3600) {
- duration = 3600;
- } else {
- mBackoff *= 2;
- }
- return duration;
- }
-
- @Override
- public void onRegistering(ISipSession session) {
- if (SAR_DBG) log("onRegistering: " + session);
- synchronized (SipService.this) {
- if (notCurrentSession(session)) return;
-
- mRegistered = false;
- mProxy.onRegistering(session);
- }
- }
-
- private boolean notCurrentSession(ISipSession session) {
- if (session != mSession) {
- ((SipSessionGroup.SipSessionImpl) session).setListener(null);
- mMyWakeLock.release(session);
- return true;
- }
- return !mRunning;
- }
-
- @Override
- public void onRegistrationDone(ISipSession session, int duration) {
- if (SAR_DBG) log("onRegistrationDone: " + session);
- synchronized (SipService.this) {
- if (notCurrentSession(session)) return;
-
- mProxy.onRegistrationDone(session, duration);
-
- if (duration > 0) {
- mExpiryTime = SystemClock.elapsedRealtime()
- + (duration * 1000);
-
- if (!mRegistered) {
- mRegistered = true;
- // allow some overlap to avoid call drop during renew
- duration -= MIN_EXPIRY_TIME;
- if (duration < MIN_EXPIRY_TIME) {
- duration = MIN_EXPIRY_TIME;
- }
- restart(duration);
-
- SipProfile localProfile = mSession.getLocalProfile();
- if ((mKeepAliveSession == null) && (isBehindNAT(mLocalIp)
- || localProfile.getSendKeepAlive())) {
- startKeepAliveProcess(getKeepAliveInterval());
- }
- }
- mMyWakeLock.release(session);
- } else {
- mRegistered = false;
- mExpiryTime = -1L;
- if (SAR_DBG) log("Refresh registration immediately");
- run();
- }
- }
- }
-
- @Override
- public void onRegistrationFailed(ISipSession session, int errorCode,
- String message) {
- if (SAR_DBG) log("onRegistrationFailed: " + session + ": "
- + SipErrorCode.toString(errorCode) + ": " + message);
- synchronized (SipService.this) {
- if (notCurrentSession(session)) return;
-
- switch (errorCode) {
- case SipErrorCode.INVALID_CREDENTIALS:
- case SipErrorCode.SERVER_UNREACHABLE:
- if (SAR_DBG) log(" pause auto-registration");
- stop();
- break;
- default:
- restartLater();
- }
-
- mErrorCode = errorCode;
- mErrorMessage = message;
- mProxy.onRegistrationFailed(session, errorCode, message);
- mMyWakeLock.release(session);
- }
- }
-
- @Override
- public void onRegistrationTimeout(ISipSession session) {
- if (SAR_DBG) log("onRegistrationTimeout: " + session);
- synchronized (SipService.this) {
- if (notCurrentSession(session)) return;
-
- mErrorCode = SipErrorCode.TIME_OUT;
- mProxy.onRegistrationTimeout(session);
- restartLater();
- mMyWakeLock.release(session);
- }
- }
-
- private void restartLater() {
- if (SAR_DBG) loge("restartLater");
- mRegistered = false;
- restart(backoffDuration());
- }
-
- private void log(String s) {
- Rlog.d(SAR_TAG, s);
- }
-
- private void loge(String s) {
- Rlog.e(SAR_TAG, s);
- }
-
- private void loge(String s, Throwable e) {
- Rlog.e(SAR_TAG, s, e);
- }
- }
-
- private class ConnectivityReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Bundle bundle = intent.getExtras();
- if (bundle != null) {
- final NetworkInfo info = (NetworkInfo)
- bundle.get(ConnectivityManager.EXTRA_NETWORK_INFO);
-
- // Run the handler in MyExecutor to be protected by wake lock
- mExecutor.execute(new Runnable() {
- @Override
- public void run() {
- onConnectivityChanged(info);
- }
- });
- }
- }
- }
-
- private void registerReceivers() {
- mContext.registerReceiver(mConnectivityReceiver,
- new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
- if (DBG) log("registerReceivers:");
- }
-
- private void unregisterReceivers() {
- mContext.unregisterReceiver(mConnectivityReceiver);
- if (DBG) log("unregisterReceivers:");
-
- // Reset variables maintained by ConnectivityReceiver.
- mWifiLock.release();
- mNetworkType = -1;
- }
-
- private void updateWakeLocks() {
- for (SipSessionGroupExt group : mSipGroups.values()) {
- if (group.isOpenedToReceiveCalls()) {
- // Also grab the WifiLock when we are disconnected, so the
- // system will keep trying to reconnect. It will be released
- // when the system eventually connects to something else.
- if (mNetworkType == ConnectivityManager.TYPE_WIFI || mNetworkType == -1) {
- mWifiLock.acquire();
- } else {
- mWifiLock.release();
- }
- return;
- }
- }
- mWifiLock.release();
- mMyWakeLock.reset(); // in case there's a leak
- }
-
- private synchronized void onConnectivityChanged(NetworkInfo info) {
- // We only care about the default network, and getActiveNetworkInfo()
- // is the only way to distinguish them. However, as broadcasts are
- // delivered asynchronously, we might miss DISCONNECTED events from
- // getActiveNetworkInfo(), which is critical to our SIP stack. To
- // solve this, if it is a DISCONNECTED event to our current network,
- // respect it. Otherwise get a new one from getActiveNetworkInfo().
- if (info == null || info.isConnected() || info.getType() != mNetworkType) {
- ConnectivityManager cm = (ConnectivityManager)
- mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- info = cm.getActiveNetworkInfo();
- }
-
- // Some devices limit SIP on Wi-Fi. In this case, if we are not on
- // Wi-Fi, treat it as a DISCONNECTED event.
- int networkType = (info != null && info.isConnected()) ? info.getType() : -1;
- if (mSipOnWifiOnly && networkType != ConnectivityManager.TYPE_WIFI) {
- networkType = -1;
- }
-
- // Ignore the event if the current active network is not changed.
- if (mNetworkType == networkType) {
- // TODO: Maybe we need to send seq/generation number
- return;
- }
- if (DBG) {
- log("onConnectivityChanged: " + mNetworkType +
- " -> " + networkType);
- }
-
- try {
- if (mNetworkType != -1) {
- mLocalIp = null;
- stopPortMappingMeasurement();
- for (SipSessionGroupExt group : mSipGroups.values()) {
- group.onConnectivityChanged(false);
- }
- }
- mNetworkType = networkType;
-
- if (mNetworkType != -1) {
- mLocalIp = determineLocalIp();
- mKeepAliveInterval = -1;
- mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
- for (SipSessionGroupExt group : mSipGroups.values()) {
- group.onConnectivityChanged(true);
- }
- }
- updateWakeLocks();
- } catch (SipException e) {
- loge("onConnectivityChanged()", e);
- }
- }
-
- private static Looper createLooper() {
- HandlerThread thread = new HandlerThread("SipService.Executor");
- thread.start();
- return thread.getLooper();
- }
-
- // Executes immediate tasks in a single thread.
- // Hold/release wake lock for running tasks
- private class MyExecutor extends Handler implements Executor {
- MyExecutor() {
- super(createLooper());
- }
-
- @Override
- public void execute(Runnable task) {
- mMyWakeLock.acquire(task);
- Message.obtain(this, 0/* don't care */, task).sendToTarget();
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.obj instanceof Runnable) {
- executeInternal((Runnable) msg.obj);
- } else {
- if (DBG) log("handleMessage: not Runnable ignore msg=" + msg);
- }
- }
-
- private void executeInternal(Runnable task) {
- try {
- task.run();
- } catch (Throwable t) {
- loge("run task: " + task, t);
- } finally {
- mMyWakeLock.release(task);
- }
- }
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-
- private static void slog(String s) {
- Rlog.d(TAG, s);
- }
-
- private void loge(String s, Throwable e) {
- Rlog.e(TAG, s, e);
- }
-}