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.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());
}
}
}

View File

@ -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);