diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 280ded6bcc94d..63352968322f3 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -524,5 +524,20 @@ public class ConnectivityManager } catch (RemoteException e) { return TETHER_ERROR_SERVICE_UNAVAIL; } - } + } + + /** + * Ensure the device stays awake until we connect with the next network + * @param forWhome The name of the network going down for logging purposes + * @return {@code true} on success, {@code false} on failure + * {@hide} + */ + public boolean requestNetworkTransitionWakelock(String forWhom) { + try { + mService.requestNetworkTransitionWakelock(forWhom); + return true; + } catch (RemoteException e) { + return false; + } + } } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index b05c2edd103ba..5a14cc9a090b5 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -72,4 +72,6 @@ interface IConnectivityManager String[] getTetherableUsbRegexs(); String[] getTetherableWifiRegexs(); + + void requestNetworkTransitionWakelock(in String forWhom); } diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java index d5bcfcd3d1998..cd8e7f1464dd6 100644 --- a/core/java/android/net/NetworkStateTracker.java +++ b/core/java/android/net/NetworkStateTracker.java @@ -38,6 +38,7 @@ public interface NetworkStateTracker { public static final int EVENT_ROAMING_CHANGED = 5; public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6; public static final int EVENT_RESTORE_DEFAULT_NETWORK = 7; + public static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8; /** * Fetch NetworkInfo for the network @@ -95,12 +96,6 @@ public interface NetworkStateTracker { */ public boolean isTeardownRequested(); - /** - * Release the wakelock, if any, that may be held while handling a - * disconnect operation. - */ - public void releaseWakeLock(); - public void startMonitoring(); /** diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index a9dbaedf20d55..5cdfc1d7758e1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -344,6 +344,13 @@ android:description="@string/permdesc_accountManagerService" android:label="@string/permlab_accountManagerService" /> + + + diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index cffcd1d1a1e7d..ec0d83c031892 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -98,6 +98,9 @@ "0,1" + + 60000 + diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 33df76c3504b0..9ff7de6152783 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -34,6 +34,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; @@ -67,7 +68,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final String NETWORK_RESTORE_DELAY_PROP_NAME = "android.telephony.apn-restore"; - private Tethering mTethering; private boolean mTetheringConfigValid = false; @@ -107,6 +107,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { private boolean mSystemReady; private Intent mInitialBroadcast; + private PowerManager.WakeLock mNetTransitionWakeLock; + private String mNetTransitionWakeLockCausedBy = ""; + private int mNetTransitionWakeLockSerialNumber; + private int mNetTransitionWakeLockTimeout; + private static class NetworkAttributes { /** * Class for holding settings read from resources. @@ -197,6 +202,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { } mContext = context; + + PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); + mNetTransitionWakeLockTimeout = mContext.getResources().getInteger( + com.android.internal.R.integer.config_networkTransitionTimeout); + mNetTrackers = new NetworkStateTracker[ ConnectivityManager.MAX_NETWORK_TYPE+1]; mHandler = new MyHandler(); @@ -878,6 +889,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { "ConnectivityService"); } + private void enforceConnectivityInternalPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CONNECTIVITY_INTERNAL, + "ConnectivityService"); + } + /** * Handle a {@code DISCONNECTED} event. If this pertains to the non-active * network, we ignore it. If it is for the active network, we send out a @@ -1153,9 +1170,17 @@ public class ConnectivityService extends IConnectivityManager.Stub { Slog.e(TAG, "Network declined teardown request"); return; } - if (isFailover) { - otherNet.releaseWakeLock(); - } + } + } + synchronized (ConnectivityService.this) { + // have a new default network, release the transition wakelock in a second + // if it's held. The second pause is to allow apps to reconnect over the + // new network + if (mNetTransitionWakeLock.isHeld()) { + mHandler.sendMessageDelayed(mHandler.obtainMessage( + NetworkStateTracker.EVENT_CLEAR_NET_TRANSITION_WAKELOCK, + mNetTransitionWakeLockSerialNumber, 0), + 1000); } } mActiveDefaultNetwork = type; @@ -1546,6 +1571,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { } pw.println(); + synchronized (this) { + pw.println("NetworkTranstionWakeLock is currently " + + (mNetTransitionWakeLock.isHeld() ? "" : "not ") + "held."); + pw.println("It was last requested for "+mNetTransitionWakeLockCausedBy); + } + pw.println(); + mTethering.dump(fd, pw, args); } @@ -1637,6 +1669,20 @@ public class ConnectivityService extends IConnectivityManager.Stub { FeatureUser u = (FeatureUser)msg.obj; u.expire(); break; + case NetworkStateTracker.EVENT_CLEAR_NET_TRANSITION_WAKELOCK: + String causedBy = null; + synchronized (ConnectivityService.this) { + if (msg.arg1 == mNetTransitionWakeLockSerialNumber && + mNetTransitionWakeLock.isHeld()) { + mNetTransitionWakeLock.release(); + causedBy = mNetTransitionWakeLockCausedBy; + } + } + if (causedBy != null) { + Slog.d(TAG, "NetTransition Wakelock for " + + causedBy + " released by timeout"); + } + break; } } } @@ -1720,4 +1766,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { Settings.Secure.TETHER_SUPPORTED, defaultVal) != 0); return tetherEnabledInSettings && mTetheringConfigValid; } + + // An API NetworkStateTrackers can call when they lose their network. + // This will automatically be cleared after X seconds or a network becomes CONNECTED, + // whichever happens first. The timer is started by the first caller and not + // restarted by subsequent callers. + public void requestNetworkTransitionWakelock(String forWhom) { + enforceConnectivityInternalPermission(); + synchronized (this) { + if (mNetTransitionWakeLock.isHeld()) return; + mNetTransitionWakeLockSerialNumber++; + mNetTransitionWakeLock.acquire(); + mNetTransitionWakeLockCausedBy = forWhom; + } + mHandler.sendMessageDelayed(mHandler.obtainMessage( + NetworkStateTracker.EVENT_CLEAR_NET_TRANSITION_WAKELOCK, + mNetTransitionWakeLockSerialNumber, 0), + mNetTransitionWakeLockTimeout); + return; + } } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 46a68ac3f03b8..6d9888b24526c 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -144,19 +144,6 @@ public class WifiService extends IWifiManager.Stub { private static final String WAKELOCK_TAG = "WifiService"; - /** - * The maximum amount of time to hold the wake lock after a disconnect - * caused by stopping the driver. Establishing an EDGE connection has been - * observed to take about 5 seconds under normal circumstances. This - * provides a bit of extra margin. - *

- * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}. - * This is the default value if a Settings.Secure value is not present. - */ - private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000; - - // Wake lock used by driver-stop operation - private static PowerManager.WakeLock sDriverStopWakeLock; // Wake lock used by other operations private static PowerManager.WakeLock sWakeLock; @@ -164,7 +151,6 @@ public class WifiService extends IWifiManager.Stub { private static final int MESSAGE_DISABLE_WIFI = 1; private static final int MESSAGE_STOP_WIFI = 2; private static final int MESSAGE_START_WIFI = 3; - private static final int MESSAGE_RELEASE_WAKELOCK = 4; private static final int MESSAGE_UPDATE_STATE = 5; private static final int MESSAGE_START_ACCESS_POINT = 6; private static final int MESSAGE_STOP_ACCESS_POINT = 7; @@ -238,7 +224,6 @@ public class WifiService extends IWifiManager.Stub { PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); - sDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); mContext.registerReceiver( new BroadcastReceiver() { @@ -293,7 +278,10 @@ public class WifiService extends IWifiManager.Stub { IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); - mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + if (mCm == null) { + mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + } + mWifiRegexs = mCm.getTetherableWifiRegexs(); for (String intf : available) { @@ -1827,19 +1815,12 @@ public class WifiService extends IWifiManager.Stub { sWakeLock.acquire(); sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY); } else if (!mWifiStateTracker.isDriverStopped()) { - int wakeLockTimeout = - Settings.Secure.getInt( - mContext.getContentResolver(), - Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS, - DEFAULT_WAKELOCK_TIMEOUT); - /* - * We are assuming that ConnectivityService can make - * a transition to cellular data within wakeLockTimeout time. - * The wakelock is released by the delayed message. - */ - sDriverStopWakeLock.acquire(); + if (mCm == null) { + mCm = (ConnectivityManager)mContext. + getSystemService(Context.CONNECTIVITY_SERVICE); + } + mCm.requestNetworkTransitionWakelock(TAG); mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI); - mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout); } } else { sWakeLock.acquire(); @@ -1925,10 +1906,6 @@ public class WifiService extends IWifiManager.Stub { // don't release wakelock break; - case MESSAGE_RELEASE_WAKELOCK: - sDriverStopWakeLock.release(); - break; - case MESSAGE_START_ACCESS_POINT: setWifiApEnabledBlocking(true, msg.arg1, diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 73cba9383dae0..2bd5640e60528 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -763,21 +763,6 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { } } - /** - * We release the wakelock in WifiService - * using a timer. - * - * TODO: - * Releasing wakelock using both timer and - * a call from ConnectivityService requires - * a rethink. We had problems where WifiService - * could keep a wakelock forever if we delete - * messages in the asynchronous call - * from ConnectivityService - */ - public void releaseWakeLock() { - } - /** * Tracks the WPA supplicant states to detect "loop" situations. * @param newSupplicantState The new WPA supplicant state.