am bd57eeaf: SipService: add wake lock for multiple components.

Merge commit 'bd57eeafe034cf850225db403700b5dc5db5ebcc' into gingerbread-plus-aosp

* commit 'bd57eeafe034cf850225db403700b5dc5db5ebcc':
  SipService: add wake lock for multiple components.
This commit is contained in:
Hung-ying Tyan
2010-10-14 02:15:23 -07:00
committed by Android Git Automerger
2 changed files with 132 additions and 68 deletions

View File

@ -39,6 +39,7 @@ import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.PowerManager;
import android.os.Process; import android.os.Process;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
@ -54,6 +55,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Timer; import java.util.Timer;
@ -93,6 +95,7 @@ public final class SipService extends ISipService.Stub {
private ConnectivityReceiver mConnectivityReceiver; private ConnectivityReceiver mConnectivityReceiver;
private boolean mWifiEnabled; private boolean mWifiEnabled;
private MyWakeLock mMyWakeLock;
/** /**
* Starts the SIP service. Do nothing if the SIP API is not supported on the * 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)); new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
context.registerReceiver(mWifiStateReceiver, context.registerReceiver(mWifiStateReceiver,
new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)); new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
mMyWakeLock = new MyWakeLock((PowerManager)
context.getSystemService(Context.POWER_SERVICE));
mTimer = new WakeupTimer(context); mTimer = new WakeupTimer(context);
mWifiOnly = SipManager.isSipWifiOnly(context); mWifiOnly = SipManager.isSipWifiOnly(context);
@ -225,7 +230,11 @@ public final class SipService extends ISipService.Stub {
group = mSipGroups.remove(localProfileUri); group = mSipGroups.remove(localProfileUri);
notifyProfileRemoved(group.getLocalProfile()); notifyProfileRemoved(group.getLocalProfile());
group.close(); group.close();
if (!anyOpened()) releaseWifiLock();
if (!anyOpened()) {
releaseWifiLock();
mMyWakeLock.reset(); // in case there's leak
}
} }
public synchronized boolean isOpened(String localProfileUri) { public synchronized boolean isOpened(String localProfileUri) {
@ -405,6 +414,8 @@ public final class SipService extends ISipService.Stub {
for (SipSessionGroupExt group : mSipGroups.values()) { for (SipSessionGroupExt group : mSipGroups.values()) {
group.onConnectivityChanged(true); group.onConnectivityChanged(true);
} }
} else {
mMyWakeLock.reset(); // in case there's a leak
} }
} catch (SipException e) { } catch (SipException e) {
Log.e(TAG, "onConnectivityChanged()", e); Log.e(TAG, "onConnectivityChanged()", e);
@ -581,7 +592,7 @@ public final class SipService extends ISipService.Stub {
} }
// KeepAliveProcess is controlled by AutoRegistrationProcess. // 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 class KeepAliveProcess implements Runnable {
private static final String TAG = "\\KEEPALIVE/"; private static final String TAG = "\\KEEPALIVE/";
private static final int INTERVAL = 10; private static final int INTERVAL = 10;
@ -600,43 +611,33 @@ public final class SipService extends ISipService.Stub {
// timeout handler // timeout handler
public void run() { 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) { synchronized (SipService.this) {
if (notCurrentSession(session)) return; if (!mRunning) return;
session = session.duplicate(); if (DEBUGV) Log.v(TAG, "~~~ keepalive: "
if (DEBUGV) Log.v(TAG, "~~~ keepalive"); + mSession.getLocalProfile().getUriString());
mTimer.cancel(this); SipSessionGroup.SipSessionImpl session = mSession.duplicate();
session.sendKeepAlive(); try {
if (session.isReRegisterRequired()) { session.sendKeepAlive();
mSession.register(EXPIRY_TIME); if (session.isReRegisterRequired()) {
} else { // Acquire wake lock for the registration process. The
mTimer.set(INTERVAL * 1000, this); // 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() { public void stop() {
if (DEBUGV && (mSession != null)) Log.v(TAG, "stop keepalive:"
+ mSession.getLocalProfile().getUriString());
mRunning = false; mRunning = false;
mSession = null; mSession = null;
mTimer.cancel(this); mTimer.cancel(this);
} }
private boolean notCurrentSession(ISipSession session) {
return (session != mSession) || !mRunning;
}
} }
private class AutoRegistrationProcess extends SipSessionAdapter 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 // start unregistration to clear up old registration at server
// TODO: when rfc5626 is deployed, use reg-id and sip.instance // TODO: when rfc5626 is deployed, use reg-id and sip.instance
// in registration to avoid adding duplicate entries to server // in registration to avoid adding duplicate entries to server
mMyWakeLock.acquire(mSession);
mSession.unregister(); mSession.unregister();
if (DEBUG) Log.d(TAG, "start AutoRegistrationProcess for " if (DEBUG) Log.d(TAG, "start AutoRegistrationProcess for "
+ mSession.getLocalProfile().getUriString()); + mSession.getLocalProfile().getUriString());
@ -676,8 +678,11 @@ public final class SipService extends ISipService.Stub {
public void stop() { public void stop() {
if (!mRunning) return; if (!mRunning) return;
mRunning = false; mRunning = false;
mSession.setListener(null); mMyWakeLock.release(mSession);
if (mConnected && mRegistered) mSession.unregister(); if (mSession != null) {
mSession.setListener(null);
if (mConnected && mRegistered) mSession.unregister();
}
mTimer.cancel(this); mTimer.cancel(this);
if (mKeepAliveProcess != null) { if (mKeepAliveProcess != null) {
@ -734,29 +739,18 @@ public final class SipService extends ISipService.Stub {
return mRegistered; return mRegistered;
} }
// timeout handler // timeout handler: re-register
public void run() { public void run() {
synchronized (SipService.this) { synchronized (SipService.this) {
if (!mRunning) return; 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; mErrorCode = SipErrorCode.NO_ERROR;
mErrorMessage = null; mErrorMessage = null;
if (DEBUG) Log.d(TAG, "~~~ registering"); 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) { private boolean notCurrentSession(ISipSession session) {
if (session != mSession) { if (session != mSession) {
((SipSessionGroup.SipSessionImpl) session).setListener(null); ((SipSessionGroup.SipSessionImpl) session).setListener(null);
mMyWakeLock.release(session);
return true; return true;
} }
return !mRunning; return !mRunning;
@ -842,6 +837,7 @@ public final class SipService extends ISipService.Stub {
mKeepAliveProcess.start(); mKeepAliveProcess.start();
} }
} }
mMyWakeLock.release(session);
} else { } else {
mRegistered = false; mRegistered = false;
mExpiryTime = -1L; mExpiryTime = -1L;
@ -872,6 +868,7 @@ public final class SipService extends ISipService.Stub {
mErrorCode = errorCode; mErrorCode = errorCode;
mErrorMessage = message; mErrorMessage = message;
mProxy.onRegistrationFailed(session, errorCode, message); mProxy.onRegistrationFailed(session, errorCode, message);
mMyWakeLock.release(session);
} }
} }
@ -884,6 +881,7 @@ public final class SipService extends ISipService.Stub {
mErrorCode = SipErrorCode.TIME_OUT; mErrorCode = SipErrorCode.TIME_OUT;
mProxy.onRegistrationTimeout(session); mProxy.onRegistrationTimeout(session);
restartLater(); restartLater();
mMyWakeLock.release(session);
} }
} }
@ -902,7 +900,16 @@ public final class SipService extends ISipService.Stub {
private MyTimerTask mTask; private MyTimerTask mTask;
@Override @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(); String action = intent.getAction();
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
Bundle b = intent.getExtras(); Bundle b = intent.getExtras();
@ -970,11 +977,13 @@ public final class SipService extends ISipService.Stub {
if (mTask != null) mTask.cancel(); if (mTask != null) mTask.cancel();
mTask = new MyTimerTask(type, connected); mTask = new MyTimerTask(type, connected);
mTimer.schedule(mTask, 2 * 1000L); mTimer.schedule(mTask, 2 * 1000L);
// TODO: hold wakup lock so that we can finish change before // hold wakup lock so that we can finish changes before the
// the device goes to sleep // device goes to sleep
mMyWakeLock.acquire(mTask);
} else { } else {
if ((mTask != null) && mTask.mNetworkType.equals(type)) { if ((mTask != null) && mTask.mNetworkType.equals(type)) {
mTask.cancel(); mTask.cancel();
mMyWakeLock.release(mTask);
} }
onConnectivityChanged(type, false); onConnectivityChanged(type, false);
} }
@ -994,7 +1003,7 @@ public final class SipService extends ISipService.Stub {
@Override @Override
public void run() { public void run() {
// delegate to mExecutor // delegate to mExecutor
getExecutor().addTask(new Runnable() { getExecutor().execute(new Runnable() {
public void run() { public void run() {
realRun(); realRun();
} }
@ -1012,6 +1021,7 @@ public final class SipService extends ISipService.Stub {
if (DEBUG) Log.d(TAG, " deliver change for " + mNetworkType if (DEBUG) Log.d(TAG, " deliver change for " + mNetworkType
+ (mConnected ? " CONNECTED" : "DISCONNECTED")); + (mConnected ? " CONNECTED" : "DISCONNECTED"));
onConnectivityChanged(mNetworkType, mConnected); onConnectivityChanged(mNetworkType, mConnected);
mMyWakeLock.release(this);
} }
} }
} }
@ -1019,7 +1029,6 @@ public final class SipService extends ISipService.Stub {
// TODO: clean up pending SipSession(s) periodically // TODO: clean up pending SipSession(s) periodically
/** /**
* Timer that can schedule events to occur even when the device is in sleep. * Timer that can schedule events to occur even when the device is in sleep.
* Only used internally in this package. * Only used internally in this package.
@ -1209,7 +1218,8 @@ public final class SipService extends ISipService.Stub {
} }
@Override @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(); String action = intent.getAction();
if (getAction().equals(action) if (getAction().equals(action)
&& intent.getExtras().containsKey(TRIGGER_TIME)) { && 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 = " if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = "
+ showTime(triggerTime) + ": " + mEventQueue.size()); + showTime(triggerTime) + ": " + mEventQueue.size());
if (stopped() || mEventQueue.isEmpty()) return; if (stopped() || mEventQueue.isEmpty()) return;
@ -1248,9 +1258,8 @@ public final class SipService extends ISipService.Stub {
event.mLastTriggerTime = event.mTriggerTime; event.mLastTriggerTime = event.mTriggerTime;
event.mTriggerTime += event.mPeriod; event.mTriggerTime += event.mPeriod;
// run the callback in a new thread to prevent deadlock // run the callback in the handler thread to prevent deadlock
new Thread(event.mCallback, "SipServiceTimerCallbackThread") getExecutor().execute(event.mCallback);
.start();
} }
if (DEBUG_TIMER) { if (DEBUG_TIMER) {
Log.d(TAG, "after timeout execution"); Log.d(TAG, "after timeout execution");
@ -1314,29 +1323,78 @@ public final class SipService extends ISipService.Stub {
} }
} }
// Single-threaded executor private static Looper createLooper() {
private static class MyExecutor extends Handler { 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() { MyExecutor() {
super(createLooper()); super(createLooper());
} }
private static Looper createLooper() { void execute(Runnable task) {
HandlerThread thread = new HandlerThread("SipService"); mMyWakeLock.acquire(task);
thread.start();
return thread.getLooper();
}
void addTask(Runnable task) {
Message.obtain(this, 0/* don't care */, task).sendToTarget(); Message.obtain(this, 0/* don't care */, task).sendToTarget();
} }
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
if (msg.obj instanceof Runnable) { if (msg.obj instanceof Runnable) {
((Runnable) msg.obj).run(); executeInternal((Runnable) msg.obj);
} else { } else {
Log.w(TAG, "can't handle msg: " + msg); 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());
}
} }
} }

View File

@ -547,8 +547,14 @@ class SipSessionGroup implements SipListener {
mState = SipSession.State.PINGING; mState = SipSession.State.PINGING;
try { try {
processCommand(new OptionsCommand()); processCommand(new OptionsCommand());
while (SipSession.State.PINGING == mState) { for (int i = 0; i < 15; i++) {
Thread.sleep(1000); 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) { } catch (SipException e) {
Log.e(TAG, "sendKeepAlive failed", e); Log.e(TAG, "sendKeepAlive failed", e);