summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--voip/java/com/android/server/sip/SipService.java190
-rw-r--r--voip/java/com/android/server/sip/SipSessionGroup.java10
2 files changed, 132 insertions, 68 deletions
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 1a17d38..f41f156 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -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;
@@ -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;
@@ -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
@@ -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);
@@ -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) {
@@ -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);
@@ -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;
@@ -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);
- session.sendKeepAlive();
- if (session.isReRegisterRequired()) {
- mSession.register(EXPIRY_TIME);
- } else {
- mTimer.set(INTERVAL * 1000, 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);
+ }
+ } 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
@@ -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());
@@ -676,8 +678,11 @@ public final class SipService extends ISipService.Stub {
public void stop() {
if (!mRunning) return;
mRunning = false;
- mSession.setListener(null);
- if (mConnected && mRegistered) mSession.unregister();
+ mMyWakeLock.release(mSession);
+ if (mSession != null) {
+ mSession.setListener(null);
+ if (mConnected && mRegistered) mSession.unregister();
+ }
mTimer.cancel(this);
if (mKeepAliveProcess != null) {
@@ -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);
+ }
}
}
@@ -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;
@@ -842,6 +837,7 @@ public final class SipService extends ISipService.Stub {
mKeepAliveProcess.start();
}
}
+ mMyWakeLock.release(session);
} else {
mRegistered = false;
mExpiryTime = -1L;
@@ -872,6 +868,7 @@ public final class SipService extends ISipService.Stub {
mErrorCode = errorCode;
mErrorMessage = message;
mProxy.onRegistrationFailed(session, errorCode, message);
+ mMyWakeLock.release(session);
}
}
@@ -884,6 +881,7 @@ public final class SipService extends ISipService.Stub {
mErrorCode = SipErrorCode.TIME_OUT;
mProxy.onRegistrationTimeout(session);
restartLater();
+ mMyWakeLock.release(session);
}
}
@@ -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();
@@ -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);
}
@@ -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();
}
@@ -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);
}
}
}
@@ -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.
@@ -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)) {
@@ -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;
@@ -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");
@@ -1314,29 +1323,78 @@ public final class SipService extends ISipService.Stub {
}
}
- // Single-threaded executor
- private static class MyExecutor extends Handler {
+ 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 {
MyExecutor() {
super(createLooper());
}
- private static Looper createLooper() {
- HandlerThread thread = new HandlerThread("SipService");
- thread.start();
- return thread.getLooper();
- }
-
- void addTask(Runnable task) {
+ 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());
+ }
}
}
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index b5f8d39..2b8058f 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -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);