From ee59e6a9fc69241b286acb7b55a22b8393c81222 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Tue, 27 Sep 2011 16:39:38 -0700 Subject: SipService: handle connectivity changes correctly. This patch assumes that for the same network type, there MUST be a DISCONNECTED between two CONNECTEDs. Also removes the Wi-Fi scanning since the framework already handles this when a WifiLock is held. Bug: 4283795 Change-Id: I08481e70c651cffcbb516c8cc6584c919078fa4f --- voip/java/com/android/server/sip/SipService.java | 363 ++++++----------------- 1 file changed, 89 insertions(+), 274 deletions(-) (limited to 'voip') diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java index f417ddd..119ed54 100644 --- a/voip/java/com/android/server/sip/SipService.java +++ b/voip/java/com/android/server/sip/SipService.java @@ -81,10 +81,8 @@ public final class SipService extends ISipService.Stub { private String mNetworkType; private boolean mConnected; private SipWakeupTimer mTimer; - private WifiScanProcess mWifiScanProcess; private WifiManager.WifiLock mWifiLock; - private boolean mWifiOnly; - private BroadcastReceiver mWifiStateReceiver = null; + private boolean mSipOnWifiOnly; private IntervalMeasurementProcess mIntervalMeasurementProcess; @@ -99,7 +97,6 @@ public final class SipService extends ISipService.Stub { new HashMap(); private ConnectivityReceiver mConnectivityReceiver; - private boolean mWifiEnabled; private SipWakeLock mMyWakeLock; private int mKeepAliveInterval; private int mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL; @@ -120,55 +117,17 @@ public final class SipService extends ISipService.Stub { if (DEBUG) Log.d(TAG, " service started!"); mContext = context; mConnectivityReceiver = new ConnectivityReceiver(); + + mWifiLock = ((WifiManager) + context.getSystemService(Context.WIFI_SERVICE)) + .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG); + mWifiLock.setReferenceCounted(false); + mSipOnWifiOnly = SipManager.isSipWifiOnly(context); + mMyWakeLock = new SipWakeLock((PowerManager) context.getSystemService(Context.POWER_SERVICE)); mTimer = new SipWakeupTimer(context, mExecutor); - mWifiOnly = SipManager.isSipWifiOnly(context); - } - - private BroadcastReceiver createWifiBroadcastReceiver() { - return new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { - int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, - WifiManager.WIFI_STATE_UNKNOWN); - synchronized (SipService.this) { - switch (state) { - case WifiManager.WIFI_STATE_ENABLED: - mWifiEnabled = true; - if (anyOpenedToReceiveCalls()) grabWifiLock(); - break; - case WifiManager.WIFI_STATE_DISABLED: - mWifiEnabled = false; - releaseWifiLock(); - break; - } - } - } - } - }; - }; - - private void registerReceivers() { - mContext.registerReceiver(mConnectivityReceiver, - new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); - if (SipManager.isSipWifiOnly(mContext)) { - mWifiStateReceiver = createWifiBroadcastReceiver(); - mContext.registerReceiver(mWifiStateReceiver, - new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)); - } - if (DEBUG) Log.d(TAG, " +++ register receivers"); - } - - private void unregisterReceivers() { - mContext.unregisterReceiver(mConnectivityReceiver); - if (SipManager.isSipWifiOnly(mContext)) { - mContext.unregisterReceiver(mWifiStateReceiver); - } - if (DEBUG) Log.d(TAG, " --- unregister receivers"); } public synchronized SipProfile[] getListOfProfiles() { @@ -218,7 +177,6 @@ public final class SipService extends ISipService.Stub { if (addingFirstProfile && !mSipGroups.isEmpty()) registerReceivers(); if (localProfile.getAutoRegistration()) { group.openToReceiveCalls(); - if (mWifiEnabled) grabWifiLock(); } } catch (SipException e) { Log.e(TAG, "openToReceiveCalls()", e); @@ -254,10 +212,9 @@ public final class SipService extends ISipService.Stub { group.close(); if (!anyOpenedToReceiveCalls()) { - releaseWifiLock(); + unregisterReceivers(); mMyWakeLock.reset(); // in case there's leak } - if (mSipGroups.isEmpty()) unregisterReceivers(); } public synchronized boolean isOpened(String localProfileUri) { @@ -388,83 +345,6 @@ public final class SipService extends ISipService.Stub { return false; } - private void grabWifiLock() { - if (mWifiLock == null) { - if (DEBUG) Log.d(TAG, "acquire wifi lock"); - mWifiLock = ((WifiManager) - mContext.getSystemService(Context.WIFI_SERVICE)) - .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG); - mWifiLock.acquire(); - if (!mConnected) startWifiScanner(); - } - } - - private void releaseWifiLock() { - if (mWifiLock != null) { - if (DEBUG) Log.d(TAG, "release wifi lock"); - mWifiLock.release(); - mWifiLock = null; - stopWifiScanner(); - } - } - - private synchronized void startWifiScanner() { - if (mWifiScanProcess == null) { - mWifiScanProcess = new WifiScanProcess(); - } - mWifiScanProcess.start(); - } - - private synchronized void stopWifiScanner() { - if (mWifiScanProcess != null) { - mWifiScanProcess.stop(); - } - } - - private synchronized void onConnectivityChanged( - String type, boolean connected) { - if (DEBUG) Log.d(TAG, "onConnectivityChanged(): " - + mNetworkType + (mConnected? " CONNECTED" : " DISCONNECTED") - + " --> " + type + (connected? " CONNECTED" : " DISCONNECTED")); - - boolean sameType = type.equals(mNetworkType); - if (!sameType && !connected) return; - - boolean wasWifi = "WIFI".equalsIgnoreCase(mNetworkType); - boolean isWifi = "WIFI".equalsIgnoreCase(type); - boolean wifiOff = (isWifi && !connected) || (wasWifi && !sameType); - boolean wifiOn = isWifi && connected; - - try { - boolean wasConnected = mConnected; - mNetworkType = type; - mConnected = connected; - - if (wasConnected) { - mLocalIp = null; - stopPortMappingMeasurement(); - for (SipSessionGroupExt group : mSipGroups.values()) { - group.onConnectivityChanged(false); - } - } - - if (connected) { - mLocalIp = determineLocalIp(); - mKeepAliveInterval = -1; - mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL; - for (SipSessionGroupExt group : mSipGroups.values()) { - group.onConnectivityChanged(true); - } - if (isWifi && (mWifiLock != null)) stopWifiScanner(); - } else { - mMyWakeLock.reset(); // in case there's a leak - if (isWifi && (mWifiLock != null)) startWifiScanner(); - } - } catch (SipException e) { - Log.e(TAG, "onConnectivityChanged()", e); - } - } - private void stopPortMappingMeasurement() { if (mIntervalMeasurementProcess != null) { mIntervalMeasurementProcess.stop(); @@ -747,36 +627,6 @@ public final class SipService extends ISipService.Stub { } } - private class WifiScanProcess implements Runnable { - private static final String TAG = "\\WIFI_SCAN/"; - private static final int INTERVAL = 60; - private boolean mRunning = false; - - private WifiManager mWifiManager; - - public void start() { - if (mRunning) return; - mRunning = true; - mTimer.set(INTERVAL * 1000, this); - } - - WifiScanProcess() { - mWifiManager = (WifiManager) - mContext.getSystemService(Context.WIFI_SERVICE); - } - - public void run() { - // scan and associate now - if (DEBUGV) Log.v(TAG, "just wake up here for wifi scanning..."); - mWifiManager.startScanActive(); - } - - public void stop() { - mRunning = false; - mTimer.cancel(this); - } - } - private class IntervalMeasurementProcess implements Runnable, SipSessionGroup.KeepAliveProcessCallback { private static final String TAG = "SipKeepAliveInterval"; @@ -1254,138 +1104,103 @@ public final class SipService extends ISipService.Stub { } private class ConnectivityReceiver extends BroadcastReceiver { - private Timer mTimer = new Timer(); - private MyTimerTask mTask; - @Override - public void onReceive(final Context context, final Intent intent) { - // Run the handler in MyExecutor to be protected by wake lock - mExecutor.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(); - if (b != null) { - NetworkInfo netInfo = (NetworkInfo) - b.get(ConnectivityManager.EXTRA_NETWORK_INFO); - String type = netInfo.getTypeName(); - NetworkInfo.State state = netInfo.getState(); - - if (mWifiOnly && (netInfo.getType() != - ConnectivityManager.TYPE_WIFI)) { - if (DEBUG) { - Log.d(TAG, "Wifi only, other connectivity ignored: " - + type); - } - return; - } + public void onReceive(Context context, Intent intent) { + Bundle bundle = intent.getExtras(); + if (bundle != null) { + final NetworkInfo info = (NetworkInfo) + bundle.get(ConnectivityManager.EXTRA_NETWORK_INFO); - NetworkInfo activeNetInfo = getActiveNetworkInfo(); - if (DEBUG) { - if (activeNetInfo != null) { - Log.d(TAG, "active network: " - + activeNetInfo.getTypeName() - + ((activeNetInfo.getState() == NetworkInfo.State.CONNECTED) - ? " CONNECTED" : " DISCONNECTED")); - } else { - Log.d(TAG, "active network: null"); - } - } - if ((state == NetworkInfo.State.CONNECTED) - && (activeNetInfo != null) - && (activeNetInfo.getType() != netInfo.getType())) { - if (DEBUG) Log.d(TAG, "ignore connect event: " + type - + ", active: " + activeNetInfo.getTypeName()); - return; - } - - if (state == NetworkInfo.State.CONNECTED) { - if (DEBUG) Log.d(TAG, "Connectivity alert: CONNECTED " + type); - onChanged(type, true); - } else if (state == NetworkInfo.State.DISCONNECTED) { - if (DEBUG) Log.d(TAG, "Connectivity alert: DISCONNECTED " + type); - onChanged(type, false); - } else { - if (DEBUG) Log.d(TAG, "Connectivity alert not processed: " - + state + " " + type); + // Run the handler in MyExecutor to be protected by wake lock + mExecutor.execute(new Runnable() { + public void run() { + onConnectivityChanged(info); } - } + }); } } + } + + private void registerReceivers() { + mContext.registerReceiver(mConnectivityReceiver, + new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); + if (DEBUG) Log.d(TAG, " +++ register receivers"); + } - private NetworkInfo getActiveNetworkInfo() { + private void unregisterReceivers() { + mContext.unregisterReceiver(mConnectivityReceiver); + if (DEBUG) Log.d(TAG, " --- unregister receivers"); + + // Reset variables maintained by ConnectivityReceiver. + mWifiLock.release(); + mConnected = false; + } + + private synchronized void onConnectivityChanged(NetworkInfo info) { + // We only care about the default network, and getActiveNetworkInfo() + // is the only way to distinguish them. However, as broadcasts are + // delivered asynchronously, we might miss DISCONNECTED events from + // getActiveNetworkInfo(), which is critical to our SIP stack. To + // solve this, if it is a DISCONNECTED event to our current network, + // respect it. Otherwise get a new one from getActiveNetworkInfo(). + if (info == null || info.isConnected() || + !info.getTypeName().equals(mNetworkType)) { ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - return cm.getActiveNetworkInfo(); + info = cm.getActiveNetworkInfo(); } - private void onChanged(String type, boolean connected) { - synchronized (SipService.this) { - // When turning on WIFI, it needs some time for network - // connectivity to get stabile so we defer good news (because - // we want to skip the interim ones) but deliver bad news - // immediately - if (connected) { - if (mTask != null) { - mTask.cancel(); - mMyWakeLock.release(mTask); - } - mTask = new MyTimerTask(type, connected); - mTimer.schedule(mTask, 2 * 1000L); - // 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); - } - } - } + // Some devices limit SIP on Wi-Fi. In this case, if we are not on + // Wi-Fi, treat it as a DISCONNECTED event. + boolean connected = (info != null && info.isConnected() && + (!mSipOnWifiOnly || info.getType() == ConnectivityManager.TYPE_WIFI)); + String networkType = connected ? info.getTypeName() : "null"; - private class MyTimerTask extends TimerTask { - private boolean mConnected; - private String mNetworkType; + // Ignore the event if the current active network is not changed. + if (connected == mConnected && networkType.equals(mNetworkType)) { + return; + } + if (DEBUG) { + Log.d(TAG, "onConnectivityChanged(): " + mNetworkType + + " -> " + networkType); + } - public MyTimerTask(String type, boolean connected) { - mNetworkType = type; - mConnected = connected; + try { + if (mConnected) { + mLocalIp = null; + stopPortMappingMeasurement(); + for (SipSessionGroupExt group : mSipGroups.values()) { + group.onConnectivityChanged(false); + } } - // timeout handler - @Override - public void run() { - // delegate to mExecutor - mExecutor.execute(new Runnable() { - public void run() { - realRun(); - } - }); - } + mConnected = connected; + mNetworkType = networkType; - private void realRun() { - synchronized (SipService.this) { - if (mTask != this) { - Log.w(TAG, " unexpected task: " + mNetworkType - + (mConnected ? " CONNECTED" : "DISCONNECTED")); - mMyWakeLock.release(this); - return; - } - mTask = null; - if (DEBUG) Log.d(TAG, " deliver change for " + mNetworkType - + (mConnected ? " CONNECTED" : "DISCONNECTED")); - onConnectivityChanged(mNetworkType, mConnected); - mMyWakeLock.release(this); + if (connected) { + mLocalIp = determineLocalIp(); + mKeepAliveInterval = -1; + mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL; + for (SipSessionGroupExt group : mSipGroups.values()) { + group.onConnectivityChanged(true); } + + // If we are on Wi-Fi, grab the WifiLock. Otherwise release it. + if (info.getType() == ConnectivityManager.TYPE_WIFI) { + mWifiLock.acquire(); + } else { + mWifiLock.release(); + } + } else { + // Always grab the WifiLock when we are disconnected, so the + // system will keep trying to reconnect. We will release it + // if we eventually connect via something else. + mWifiLock.acquire(); + + mMyWakeLock.reset(); // in case there's a leak } + } catch (SipException e) { + Log.e(TAG, "onConnectivityChanged()", e); } } -- cgit v1.1