summaryrefslogtreecommitdiffstats
path: root/voip
diff options
context:
space:
mode:
authorHung-ying Tyan <tyanh@google.com>2010-10-13 23:32:17 +0800
committerHung-ying Tyan <tyanh@google.com>2010-10-14 16:54:47 +0800
commitbd57eeafe034cf850225db403700b5dc5db5ebcc (patch)
tree052db2ebe953138c06360cf02422ee38a80d21ae /voip
parent3bb1044243ad38da35ccddc3e0d6d92d64fefe3e (diff)
downloadframeworks_base-bd57eeafe034cf850225db403700b5dc5db5ebcc.zip
frameworks_base-bd57eeafe034cf850225db403700b5dc5db5ebcc.tar.gz
frameworks_base-bd57eeafe034cf850225db403700b5dc5db5ebcc.tar.bz2
SipService: add wake lock for multiple components.
+ Add MyWakeLock to maintain a global wake lock for multiple components. + Use a Set to store components that want to hold the lock. + When the first component enters the set, we grab the global wake lock. + When the set becomes empty, we release the global lock. + In places like no account being opened to receive calls, we reset the wake lock just to be safe from possible leakage. + Make MyExecutor aware of the wake lock. It will grab the wake lock on behalf of the task so that tasks don't need to worry about the lock. + Connectivity receiver is modified to be executed in MyExecutor. + WakeupTimer handler is already protected by AlarmManager's wake lock but all the timeout handlers that register themselves to the WakeupTimer are to be executed in MyExecutor to be protected by the wake lock. + Remove unnecessary code in the Keepalive and registration processes. Since both processes are executed in MyExecutor submitted by the WakeupTimer (as they are timeout handlers registered to the WakeupTimer), they don't need to add themselves to MyExecutor explicitly in their run() callbacks. + Make the keepalive process wait for at most 3 seconds instead of forever for server response. It could cause the wake lock to be held longer than necessary and is a potential cause for ANR. http://b/issue?id=3081828 Related bug: http://b/issue?id=3087153 Change-Id: Idee0ddb837e67daa0d5092c012bb242bd7c18431
Diffstat (limited to 'voip')
-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);