Loading voip/java/com/android/server/sip/SipService.java +124 −66 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ 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; Loading @@ -54,6 +55,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Timer; Loading Loading @@ -93,6 +95,7 @@ public final class SipService extends ISipService.Stub { private ConnectivityReceiver mConnectivityReceiver; private boolean mWifiEnabled; private MyWakeLock mMyWakeLock; /** * Starts the SIP service. Do nothing if the SIP API is not supported on the Loading @@ -114,6 +117,8 @@ public final class SipService extends ISipService.Stub { new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); context.registerReceiver(mWifiStateReceiver, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)); mMyWakeLock = new MyWakeLock((PowerManager) context.getSystemService(Context.POWER_SERVICE)); mTimer = new WakeupTimer(context); mWifiOnly = SipManager.isSipWifiOnly(context); Loading Loading @@ -225,7 +230,11 @@ public final class SipService extends ISipService.Stub { group = mSipGroups.remove(localProfileUri); notifyProfileRemoved(group.getLocalProfile()); group.close(); if (!anyOpened()) releaseWifiLock(); if (!anyOpened()) { releaseWifiLock(); mMyWakeLock.reset(); // in case there's leak } } public synchronized boolean isOpened(String localProfileUri) { Loading Loading @@ -405,6 +414,8 @@ public final class SipService extends ISipService.Stub { for (SipSessionGroupExt group : mSipGroups.values()) { group.onConnectivityChanged(true); } } else { mMyWakeLock.reset(); // in case there's a leak } } catch (SipException e) { Log.e(TAG, "onConnectivityChanged()", e); Loading Loading @@ -581,7 +592,7 @@ public final class SipService extends ISipService.Stub { } // KeepAliveProcess is controlled by AutoRegistrationProcess. // All methods will be invoked in sync with SipService.this except realRun() // All methods will be invoked in sync with SipService.this. private class KeepAliveProcess implements Runnable { private static final String TAG = "\\KEEPALIVE/"; private static final int INTERVAL = 10; Loading @@ -600,43 +611,33 @@ public final class SipService extends ISipService.Stub { // timeout handler public void run() { if (!mRunning) return; final SipSessionGroup.SipSessionImpl session = mSession; // delegate to mExecutor getExecutor().addTask(new Runnable() { public void run() { realRun(session); } }); } // real timeout handler private void realRun(SipSessionGroup.SipSessionImpl session) { synchronized (SipService.this) { if (notCurrentSession(session)) return; if (!mRunning) return; session = session.duplicate(); if (DEBUGV) Log.v(TAG, "~~~ keepalive"); mTimer.cancel(this); if (DEBUGV) Log.v(TAG, "~~~ keepalive: " + mSession.getLocalProfile().getUriString()); SipSessionGroup.SipSessionImpl session = mSession.duplicate(); try { session.sendKeepAlive(); if (session.isReRegisterRequired()) { // Acquire wake lock for the registration process. The // lock will be released when registration is complete. mMyWakeLock.acquire(mSession); mSession.register(EXPIRY_TIME); } else { mTimer.set(INTERVAL * 1000, this); } } catch (Throwable t) { Log.w(TAG, "keepalive error: " + t); } } } public void stop() { if (DEBUGV && (mSession != null)) Log.v(TAG, "stop keepalive:" + mSession.getLocalProfile().getUriString()); mRunning = false; mSession = null; mTimer.cancel(this); } private boolean notCurrentSession(ISipSession session) { return (session != mSession) || !mRunning; } } private class AutoRegistrationProcess extends SipSessionAdapter Loading Loading @@ -667,6 +668,7 @@ public final class SipService extends ISipService.Stub { // 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(); if (DEBUG) Log.d(TAG, "start AutoRegistrationProcess for " + mSession.getLocalProfile().getUriString()); Loading @@ -676,8 +678,11 @@ public final class SipService extends ISipService.Stub { public void stop() { if (!mRunning) return; mRunning = false; mMyWakeLock.release(mSession); if (mSession != null) { mSession.setListener(null); if (mConnected && mRegistered) mSession.unregister(); } mTimer.cancel(this); if (mKeepAliveProcess != null) { Loading Loading @@ -734,29 +739,18 @@ public final class SipService extends ISipService.Stub { return mRegistered; } // timeout handler // timeout handler: re-register public void run() { synchronized (SipService.this) { if (!mRunning) return; final SipSessionGroup.SipSessionImpl session = mSession; // delegate to mExecutor getExecutor().addTask(new Runnable() { public void run() { realRun(session); } }); } } // real timeout handler private void realRun(SipSessionGroup.SipSessionImpl session) { synchronized (SipService.this) { if (notCurrentSession(session)) return; mErrorCode = SipErrorCode.NO_ERROR; mErrorMessage = null; if (DEBUG) Log.d(TAG, "~~~ registering"); if (mConnected) session.register(EXPIRY_TIME); if (mConnected) { mMyWakeLock.acquire(mSession); mSession.register(EXPIRY_TIME); } } } Loading Loading @@ -806,6 +800,7 @@ public final class SipService extends ISipService.Stub { private boolean notCurrentSession(ISipSession session) { if (session != mSession) { ((SipSessionGroup.SipSessionImpl) session).setListener(null); mMyWakeLock.release(session); return true; } return !mRunning; Loading Loading @@ -842,6 +837,7 @@ public final class SipService extends ISipService.Stub { mKeepAliveProcess.start(); } } mMyWakeLock.release(session); } else { mRegistered = false; mExpiryTime = -1L; Loading Loading @@ -872,6 +868,7 @@ public final class SipService extends ISipService.Stub { mErrorCode = errorCode; mErrorMessage = message; mProxy.onRegistrationFailed(session, errorCode, message); mMyWakeLock.release(session); } } Loading @@ -884,6 +881,7 @@ public final class SipService extends ISipService.Stub { mErrorCode = SipErrorCode.TIME_OUT; mProxy.onRegistrationTimeout(session); restartLater(); mMyWakeLock.release(session); } } Loading @@ -902,7 +900,16 @@ public final class SipService extends ISipService.Stub { private MyTimerTask mTask; @Override public void onReceive(Context context, Intent intent) { public void onReceive(final Context context, final Intent intent) { // Run the handler in MyExecutor to be protected by wake lock getExecutor().execute(new Runnable() { public void run() { onReceiveInternal(context, intent); } }); } private void onReceiveInternal(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { Bundle b = intent.getExtras(); Loading Loading @@ -970,11 +977,13 @@ public final class SipService extends ISipService.Stub { if (mTask != null) mTask.cancel(); mTask = new MyTimerTask(type, connected); mTimer.schedule(mTask, 2 * 1000L); // TODO: hold wakup lock so that we can finish change before // the device goes to sleep // hold wakup lock so that we can finish changes before the // device goes to sleep mMyWakeLock.acquire(mTask); } else { if ((mTask != null) && mTask.mNetworkType.equals(type)) { mTask.cancel(); mMyWakeLock.release(mTask); } onConnectivityChanged(type, false); } Loading @@ -994,7 +1003,7 @@ public final class SipService extends ISipService.Stub { @Override public void run() { // delegate to mExecutor getExecutor().addTask(new Runnable() { getExecutor().execute(new Runnable() { public void run() { realRun(); } Loading @@ -1012,6 +1021,7 @@ public final class SipService extends ISipService.Stub { if (DEBUG) Log.d(TAG, " deliver change for " + mNetworkType + (mConnected ? " CONNECTED" : "DISCONNECTED")); onConnectivityChanged(mNetworkType, mConnected); mMyWakeLock.release(this); } } } Loading @@ -1019,7 +1029,6 @@ public final class SipService extends ISipService.Stub { // TODO: clean up pending SipSession(s) periodically /** * Timer that can schedule events to occur even when the device is in sleep. * Only used internally in this package. Loading Loading @@ -1209,7 +1218,8 @@ public final class SipService extends ISipService.Stub { } @Override public synchronized void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) { // This callback is already protected by AlarmManager's wake lock. String action = intent.getAction(); if (getAction().equals(action) && intent.getExtras().containsKey(TRIGGER_TIME)) { Loading @@ -1236,7 +1246,7 @@ public final class SipService extends ISipService.Stub { } } private void execute(long triggerTime) { private synchronized void execute(long triggerTime) { if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = " + showTime(triggerTime) + ": " + mEventQueue.size()); if (stopped() || mEventQueue.isEmpty()) return; Loading @@ -1248,9 +1258,8 @@ public final class SipService extends ISipService.Stub { event.mLastTriggerTime = event.mTriggerTime; event.mTriggerTime += event.mPeriod; // run the callback in a new thread to prevent deadlock new Thread(event.mCallback, "SipServiceTimerCallbackThread") .start(); // run the callback in the handler thread to prevent deadlock getExecutor().execute(event.mCallback); } if (DEBUG_TIMER) { Log.d(TAG, "after timeout execution"); Loading Loading @@ -1314,29 +1323,78 @@ public final class SipService extends ISipService.Stub { } } // Single-threaded executor private static class MyExecutor extends Handler { MyExecutor() { super(createLooper()); } private static Looper createLooper() { HandlerThread thread = new HandlerThread("SipService"); HandlerThread thread = new HandlerThread("SipService.Executor"); thread.start(); return thread.getLooper(); } void addTask(Runnable task) { // Executes immediate tasks in a single thread. // Hold/release wake lock for running tasks private class MyExecutor extends Handler { MyExecutor() { super(createLooper()); } 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) { ((Runnable) msg.obj).run(); executeInternal((Runnable) msg.obj); } else { Log.w(TAG, "can't handle msg: " + msg); } } private void executeInternal(Runnable task) { try { task.run(); } catch (Throwable t) { Log.e(TAG, "run task: " + task, t); } finally { mMyWakeLock.release(task); } } } private static class MyWakeLock { private PowerManager mPowerManager; private PowerManager.WakeLock mWakeLock; private HashSet<Object> mHolders = new HashSet<Object>(); MyWakeLock(PowerManager powerManager) { mPowerManager = powerManager; } synchronized void reset() { mHolders.clear(); release(null); if (DEBUGV) Log.v(TAG, "~~~ hard reset wakelock"); } synchronized void acquire(Object holder) { mHolders.add(holder); if (mWakeLock == null) { mWakeLock = mPowerManager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, "SipWakeLock"); } if (!mWakeLock.isHeld()) mWakeLock.acquire(); if (DEBUGV) Log.v(TAG, "acquire wakelock: holder count=" + mHolders.size()); } synchronized void release(Object holder) { mHolders.remove(holder); if ((mWakeLock != null) && mHolders.isEmpty() && mWakeLock.isHeld()) { mWakeLock.release(); } if (DEBUGV) Log.v(TAG, "release wakelock: holder count=" + mHolders.size()); } } } voip/java/com/android/server/sip/SipSessionGroup.java +8 −2 Original line number Diff line number Diff line Loading @@ -547,8 +547,14 @@ class SipSessionGroup implements SipListener { mState = SipSession.State.PINGING; try { processCommand(new OptionsCommand()); while (SipSession.State.PINGING == mState) { Thread.sleep(1000); for (int i = 0; i < 15; i++) { if (SipSession.State.PINGING != mState) break; Thread.sleep(200); } if (SipSession.State.PINGING == mState) { // FIXME: what to do if server doesn't respond reset(); if (DEBUG) Log.w(TAG, "no response from ping"); } } catch (SipException e) { Log.e(TAG, "sendKeepAlive failed", e); Loading Loading
voip/java/com/android/server/sip/SipService.java +124 −66 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ 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; Loading @@ -54,6 +55,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Timer; Loading Loading @@ -93,6 +95,7 @@ public final class SipService extends ISipService.Stub { private ConnectivityReceiver mConnectivityReceiver; private boolean mWifiEnabled; private MyWakeLock mMyWakeLock; /** * Starts the SIP service. Do nothing if the SIP API is not supported on the Loading @@ -114,6 +117,8 @@ public final class SipService extends ISipService.Stub { new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); context.registerReceiver(mWifiStateReceiver, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)); mMyWakeLock = new MyWakeLock((PowerManager) context.getSystemService(Context.POWER_SERVICE)); mTimer = new WakeupTimer(context); mWifiOnly = SipManager.isSipWifiOnly(context); Loading Loading @@ -225,7 +230,11 @@ public final class SipService extends ISipService.Stub { group = mSipGroups.remove(localProfileUri); notifyProfileRemoved(group.getLocalProfile()); group.close(); if (!anyOpened()) releaseWifiLock(); if (!anyOpened()) { releaseWifiLock(); mMyWakeLock.reset(); // in case there's leak } } public synchronized boolean isOpened(String localProfileUri) { Loading Loading @@ -405,6 +414,8 @@ public final class SipService extends ISipService.Stub { for (SipSessionGroupExt group : mSipGroups.values()) { group.onConnectivityChanged(true); } } else { mMyWakeLock.reset(); // in case there's a leak } } catch (SipException e) { Log.e(TAG, "onConnectivityChanged()", e); Loading Loading @@ -581,7 +592,7 @@ public final class SipService extends ISipService.Stub { } // KeepAliveProcess is controlled by AutoRegistrationProcess. // All methods will be invoked in sync with SipService.this except realRun() // All methods will be invoked in sync with SipService.this. private class KeepAliveProcess implements Runnable { private static final String TAG = "\\KEEPALIVE/"; private static final int INTERVAL = 10; Loading @@ -600,43 +611,33 @@ public final class SipService extends ISipService.Stub { // timeout handler public void run() { if (!mRunning) return; final SipSessionGroup.SipSessionImpl session = mSession; // delegate to mExecutor getExecutor().addTask(new Runnable() { public void run() { realRun(session); } }); } // real timeout handler private void realRun(SipSessionGroup.SipSessionImpl session) { synchronized (SipService.this) { if (notCurrentSession(session)) return; if (!mRunning) return; session = session.duplicate(); if (DEBUGV) Log.v(TAG, "~~~ keepalive"); mTimer.cancel(this); if (DEBUGV) Log.v(TAG, "~~~ keepalive: " + mSession.getLocalProfile().getUriString()); SipSessionGroup.SipSessionImpl session = mSession.duplicate(); try { session.sendKeepAlive(); if (session.isReRegisterRequired()) { // Acquire wake lock for the registration process. The // lock will be released when registration is complete. mMyWakeLock.acquire(mSession); mSession.register(EXPIRY_TIME); } else { mTimer.set(INTERVAL * 1000, this); } } catch (Throwable t) { Log.w(TAG, "keepalive error: " + t); } } } public void stop() { if (DEBUGV && (mSession != null)) Log.v(TAG, "stop keepalive:" + mSession.getLocalProfile().getUriString()); mRunning = false; mSession = null; mTimer.cancel(this); } private boolean notCurrentSession(ISipSession session) { return (session != mSession) || !mRunning; } } private class AutoRegistrationProcess extends SipSessionAdapter Loading Loading @@ -667,6 +668,7 @@ public final class SipService extends ISipService.Stub { // 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(); if (DEBUG) Log.d(TAG, "start AutoRegistrationProcess for " + mSession.getLocalProfile().getUriString()); Loading @@ -676,8 +678,11 @@ public final class SipService extends ISipService.Stub { public void stop() { if (!mRunning) return; mRunning = false; mMyWakeLock.release(mSession); if (mSession != null) { mSession.setListener(null); if (mConnected && mRegistered) mSession.unregister(); } mTimer.cancel(this); if (mKeepAliveProcess != null) { Loading Loading @@ -734,29 +739,18 @@ public final class SipService extends ISipService.Stub { return mRegistered; } // timeout handler // timeout handler: re-register public void run() { synchronized (SipService.this) { if (!mRunning) return; final SipSessionGroup.SipSessionImpl session = mSession; // delegate to mExecutor getExecutor().addTask(new Runnable() { public void run() { realRun(session); } }); } } // real timeout handler private void realRun(SipSessionGroup.SipSessionImpl session) { synchronized (SipService.this) { if (notCurrentSession(session)) return; mErrorCode = SipErrorCode.NO_ERROR; mErrorMessage = null; if (DEBUG) Log.d(TAG, "~~~ registering"); if (mConnected) session.register(EXPIRY_TIME); if (mConnected) { mMyWakeLock.acquire(mSession); mSession.register(EXPIRY_TIME); } } } Loading Loading @@ -806,6 +800,7 @@ public final class SipService extends ISipService.Stub { private boolean notCurrentSession(ISipSession session) { if (session != mSession) { ((SipSessionGroup.SipSessionImpl) session).setListener(null); mMyWakeLock.release(session); return true; } return !mRunning; Loading Loading @@ -842,6 +837,7 @@ public final class SipService extends ISipService.Stub { mKeepAliveProcess.start(); } } mMyWakeLock.release(session); } else { mRegistered = false; mExpiryTime = -1L; Loading Loading @@ -872,6 +868,7 @@ public final class SipService extends ISipService.Stub { mErrorCode = errorCode; mErrorMessage = message; mProxy.onRegistrationFailed(session, errorCode, message); mMyWakeLock.release(session); } } Loading @@ -884,6 +881,7 @@ public final class SipService extends ISipService.Stub { mErrorCode = SipErrorCode.TIME_OUT; mProxy.onRegistrationTimeout(session); restartLater(); mMyWakeLock.release(session); } } Loading @@ -902,7 +900,16 @@ public final class SipService extends ISipService.Stub { private MyTimerTask mTask; @Override public void onReceive(Context context, Intent intent) { public void onReceive(final Context context, final Intent intent) { // Run the handler in MyExecutor to be protected by wake lock getExecutor().execute(new Runnable() { public void run() { onReceiveInternal(context, intent); } }); } private void onReceiveInternal(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { Bundle b = intent.getExtras(); Loading Loading @@ -970,11 +977,13 @@ public final class SipService extends ISipService.Stub { if (mTask != null) mTask.cancel(); mTask = new MyTimerTask(type, connected); mTimer.schedule(mTask, 2 * 1000L); // TODO: hold wakup lock so that we can finish change before // the device goes to sleep // hold wakup lock so that we can finish changes before the // device goes to sleep mMyWakeLock.acquire(mTask); } else { if ((mTask != null) && mTask.mNetworkType.equals(type)) { mTask.cancel(); mMyWakeLock.release(mTask); } onConnectivityChanged(type, false); } Loading @@ -994,7 +1003,7 @@ public final class SipService extends ISipService.Stub { @Override public void run() { // delegate to mExecutor getExecutor().addTask(new Runnable() { getExecutor().execute(new Runnable() { public void run() { realRun(); } Loading @@ -1012,6 +1021,7 @@ public final class SipService extends ISipService.Stub { if (DEBUG) Log.d(TAG, " deliver change for " + mNetworkType + (mConnected ? " CONNECTED" : "DISCONNECTED")); onConnectivityChanged(mNetworkType, mConnected); mMyWakeLock.release(this); } } } Loading @@ -1019,7 +1029,6 @@ public final class SipService extends ISipService.Stub { // TODO: clean up pending SipSession(s) periodically /** * Timer that can schedule events to occur even when the device is in sleep. * Only used internally in this package. Loading Loading @@ -1209,7 +1218,8 @@ public final class SipService extends ISipService.Stub { } @Override public synchronized void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) { // This callback is already protected by AlarmManager's wake lock. String action = intent.getAction(); if (getAction().equals(action) && intent.getExtras().containsKey(TRIGGER_TIME)) { Loading @@ -1236,7 +1246,7 @@ public final class SipService extends ISipService.Stub { } } private void execute(long triggerTime) { private synchronized void execute(long triggerTime) { if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = " + showTime(triggerTime) + ": " + mEventQueue.size()); if (stopped() || mEventQueue.isEmpty()) return; Loading @@ -1248,9 +1258,8 @@ public final class SipService extends ISipService.Stub { event.mLastTriggerTime = event.mTriggerTime; event.mTriggerTime += event.mPeriod; // run the callback in a new thread to prevent deadlock new Thread(event.mCallback, "SipServiceTimerCallbackThread") .start(); // run the callback in the handler thread to prevent deadlock getExecutor().execute(event.mCallback); } if (DEBUG_TIMER) { Log.d(TAG, "after timeout execution"); Loading Loading @@ -1314,29 +1323,78 @@ public final class SipService extends ISipService.Stub { } } // Single-threaded executor private static class MyExecutor extends Handler { MyExecutor() { super(createLooper()); } private static Looper createLooper() { HandlerThread thread = new HandlerThread("SipService"); HandlerThread thread = new HandlerThread("SipService.Executor"); thread.start(); return thread.getLooper(); } void addTask(Runnable task) { // Executes immediate tasks in a single thread. // Hold/release wake lock for running tasks private class MyExecutor extends Handler { MyExecutor() { super(createLooper()); } 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) { ((Runnable) msg.obj).run(); executeInternal((Runnable) msg.obj); } else { Log.w(TAG, "can't handle msg: " + msg); } } private void executeInternal(Runnable task) { try { task.run(); } catch (Throwable t) { Log.e(TAG, "run task: " + task, t); } finally { mMyWakeLock.release(task); } } } private static class MyWakeLock { private PowerManager mPowerManager; private PowerManager.WakeLock mWakeLock; private HashSet<Object> mHolders = new HashSet<Object>(); MyWakeLock(PowerManager powerManager) { mPowerManager = powerManager; } synchronized void reset() { mHolders.clear(); release(null); if (DEBUGV) Log.v(TAG, "~~~ hard reset wakelock"); } synchronized void acquire(Object holder) { mHolders.add(holder); if (mWakeLock == null) { mWakeLock = mPowerManager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, "SipWakeLock"); } if (!mWakeLock.isHeld()) mWakeLock.acquire(); if (DEBUGV) Log.v(TAG, "acquire wakelock: holder count=" + mHolders.size()); } synchronized void release(Object holder) { mHolders.remove(holder); if ((mWakeLock != null) && mHolders.isEmpty() && mWakeLock.isHeld()) { mWakeLock.release(); } if (DEBUGV) Log.v(TAG, "release wakelock: holder count=" + mHolders.size()); } } }
voip/java/com/android/server/sip/SipSessionGroup.java +8 −2 Original line number Diff line number Diff line Loading @@ -547,8 +547,14 @@ class SipSessionGroup implements SipListener { mState = SipSession.State.PINGING; try { processCommand(new OptionsCommand()); while (SipSession.State.PINGING == mState) { Thread.sleep(1000); for (int i = 0; i < 15; i++) { if (SipSession.State.PINGING != mState) break; Thread.sleep(200); } if (SipSession.State.PINGING == mState) { // FIXME: what to do if server doesn't respond reset(); if (DEBUG) Log.w(TAG, "no response from ping"); } } catch (SipException e) { Log.e(TAG, "sendKeepAlive failed", e); Loading