Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4f5eb955 authored by Hung-ying Tyan's avatar Hung-ying Tyan Committed by Android Git Automerger
Browse files

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

Merge commit 'bd57eeaf' into gingerbread-plus-aosp

* commit 'bd57eeaf':
  SipService: add wake lock for multiple components.
parents f3a935f6 bd57eeaf
Loading
Loading
Loading
Loading
+124 −66
Original line number Diff line number Diff line
@@ -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);
                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);
                } else {
                    mTimer.set(INTERVAL * 1000, this);
                    }
                } 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;
            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 {
        MyExecutor() {
            super(createLooper());
        }

    private static Looper createLooper() {
            HandlerThread thread = new HandlerThread("SipService");
        HandlerThread thread = new HandlerThread("SipService.Executor");
        thread.start();
        return thread.getLooper();
    }

        void addTask(Runnable task) {
    // Executes immediate tasks in a single thread.
    // Hold/release wake lock for running tasks
    private class MyExecutor extends Handler {
        MyExecutor() {
            super(createLooper());
        }

        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());
        }
    }
}
+8 −2
Original line number Diff line number Diff line
@@ -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);