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:
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
Reference in New Issue
Block a user