From d2127c438fc858a4f6c8ad75b7a1e4491da69a78 Mon Sep 17 00:00:00 2001 From: Irfan Sheriff Date: Fri, 22 Apr 2011 12:20:38 -0700 Subject: [PATCH] Handle DHCP renewal and wakeup in framework The native DHCP client renews IP address, but can fail to do so when the device is in suspend. This has lead to issues where device fails to renew in time and on networks with lots of devices re-using expired DHCP IP addresses, it leads to severe issues. Handle DHCP renewal and wakeup in framework Bug: 3344732 Change-Id: Ie4062e04a477f4a233946155e40a7c999b337c3f --- wifi/java/android/net/wifi/WifiStateTracker.java | 153 ++++++++++++++++++----- 1 file changed, 122 insertions(+), 31 deletions(-) diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 27e6a72ec977..f3132594c7e1 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -23,6 +23,18 @@ import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; import android.app.ActivityManagerNative; +import android.app.AlarmManager; +import android.app.Notification; +import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothA2dp; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; import android.net.NetworkInfo; import android.net.NetworkStateTracker; import android.net.DhcpInfo; @@ -32,8 +44,10 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.State; import android.os.Message; import android.os.Parcelable; +import android.os.PowerManager; import android.os.Handler; import android.os.HandlerThread; +import android.os.SystemClock; import android.os.SystemProperties; import android.os.Looper; import android.os.RemoteException; @@ -44,15 +58,6 @@ import android.text.TextUtils; import android.util.EventLog; import android.util.Log; import android.util.Config; -import android.app.Notification; -import android.app.PendingIntent; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHeadset; -import android.bluetooth.BluetoothA2dp; -import android.content.ContentResolver; -import android.content.Intent; -import android.content.Context; -import android.database.ContentObserver; import com.android.internal.app.IBatteryStats; import java.net.UnknownHostException; @@ -91,15 +96,16 @@ public class WifiStateTracker extends NetworkStateTracker { private static final int EVENT_INTERFACE_CONFIGURATION_FAILED = 7; private static final int EVENT_POLL_INTERVAL = 8; private static final int EVENT_DHCP_START = 9; - private static final int EVENT_DEFERRED_DISCONNECT = 10; - private static final int EVENT_DEFERRED_RECONNECT = 11; + private static final int EVENT_DHCP_RENEW = 10; + private static final int EVENT_DEFERRED_DISCONNECT = 11; + private static final int EVENT_DEFERRED_RECONNECT = 12; /** * The driver is started or stopped. The object will be the state: true for * started, false for stopped. */ - private static final int EVENT_DRIVER_STATE_CHANGED = 12; - private static final int EVENT_PASSWORD_KEY_MAY_BE_INCORRECT = 13; - private static final int EVENT_MAYBE_START_SCAN_POST_DISCONNECT = 14; + private static final int EVENT_DRIVER_STATE_CHANGED = 13; + private static final int EVENT_PASSWORD_KEY_MAY_BE_INCORRECT = 14; + private static final int EVENT_MAYBE_START_SCAN_POST_DISCONNECT = 15; /** * The driver state indication. @@ -218,6 +224,15 @@ public class WifiStateTracker extends NetworkStateTracker { private boolean mUseStaticIp = false; private int mReconnectCount; + private AlarmManager mAlarmManager; + private PendingIntent mDhcpRenewalIntent; + private PowerManager.WakeLock mDhcpRenewWakeLock; + private static final String WAKELOCK_TAG = "*wifi*"; + + private static final int DHCP_RENEW = 0; + private static final String ACTION_DHCP_RENEW = "android.net.wifi.DHCP_RENEW"; + + /* Tracks if any network in the configuration is disabled */ private AtomicBoolean mIsAnyNetworkDisabled = new AtomicBoolean(false); @@ -385,6 +400,27 @@ public class WifiStateTracker extends NetworkStateTracker { mDhcpInfo = new DhcpInfo(); mRunState = RUN_STATE_STARTING; + mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + Intent dhcpRenewalIntent = new Intent(ACTION_DHCP_RENEW, null); + mDhcpRenewalIntent = PendingIntent.getBroadcast(mContext, DHCP_RENEW, dhcpRenewalIntent, 0); + + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + //DHCP renew + if (mDhcpTarget != null) { + Log.d(TAG, "Sending a DHCP renewal"); + //acquire a 40s wakelock to finish DHCP renewal + mDhcpRenewWakeLock.acquire(40000); + mDhcpTarget.sendEmptyMessage(EVENT_DHCP_RENEW); + } + } + },new IntentFilter(ACTION_DHCP_RENEW)); + + PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mDhcpRenewWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); + // Setting is in seconds NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l; @@ -2388,7 +2424,7 @@ public class WifiStateTracker extends NetworkStateTracker { private class DhcpHandler extends Handler { - private Handler mTarget; + private Handler mWifiStateTrackerHandler; /** * Whether to skip the DHCP result callback to the target. For example, @@ -2409,10 +2445,10 @@ public class WifiStateTracker extends NetworkStateTracker { * in an error state and we will not disable coexistence. */ private BluetoothHeadset mBluetoothHeadset; - + public DhcpHandler(Looper looper, Handler target) { super(looper); - mTarget = target; + mWifiStateTrackerHandler = target; mBluetoothHeadset = new BluetoothHeadset(mContext, null); } @@ -2422,7 +2458,7 @@ public class WifiStateTracker extends NetworkStateTracker { switch (msg.what) { case EVENT_DHCP_START: - + case EVENT_DHCP_RENEW: boolean modifiedBluetoothCoexistenceMode = false; int powerMode = DRIVER_POWER_MODE_AUTO; @@ -2464,14 +2500,70 @@ public class WifiStateTracker extends NetworkStateTracker { // A new request is being made, so assume we will callback mCancelCallback = false; } - Log.d(TAG, "DhcpHandler: DHCP request started"); - if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) { - event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED; - if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded"); - } else { - event = EVENT_INTERFACE_CONFIGURATION_FAILED; - Log.i(TAG, "DhcpHandler: DHCP request failed: " + - NetworkUtils.getDhcpError()); + + if (msg.what == EVENT_DHCP_START) { + Log.d(TAG, "DHCP request started"); + if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) { + event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED; + Log.d(TAG, "DHCP succeeded with lease: " + mDhcpInfo.leaseDuration); + //Do it a bit earlier than half the lease duration time + //to beat the native DHCP client and avoid extra packets + //48% for one hour lease time = 29 minutes + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + + mDhcpInfo.leaseDuration * 480, //in milliseconds + mDhcpRenewalIntent); + } else { + event = EVENT_INTERFACE_CONFIGURATION_FAILED; + Log.e(TAG, "DHCP request failed: " + NetworkUtils.getDhcpError()); + } + synchronized (this) { + if (!mCancelCallback) { + mWifiStateTrackerHandler.sendEmptyMessage(event); + } + } + + } else if (msg.what == EVENT_DHCP_RENEW) { + Log.d(TAG, "DHCP renewal started"); + int oIp = mDhcpInfo.ipAddress; + int oGw = mDhcpInfo.gateway; + int oMsk = mDhcpInfo.netmask; + int oDns1 = mDhcpInfo.dns1; + int oDns2 = mDhcpInfo.dns2; + + if (NetworkUtils.runDhcpRenew(mInterfaceName, mDhcpInfo)) { + Log.d(TAG, "DHCP renewal with lease: " + mDhcpInfo.leaseDuration); + + boolean changed = + (oIp != mDhcpInfo.ipAddress || + oGw != mDhcpInfo.gateway || + oMsk != mDhcpInfo.netmask || + oDns1 != mDhcpInfo.dns1 || + oDns2 != mDhcpInfo.dns2); + + if (changed) { + Log.d(TAG, "IP config change on renewal"); + mWifiInfo.setIpAddress(mDhcpInfo.ipAddress); + NetworkUtils.resetConnections(mInterfaceName); + msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, + mNetworkInfo); + msg.sendToTarget(); + } + + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + + mDhcpInfo.leaseDuration * 480, + mDhcpRenewalIntent); + } else { + event = EVENT_INTERFACE_CONFIGURATION_FAILED; + Log.d(TAG, "DHCP renewal failed: " + NetworkUtils.getDhcpError()); + + synchronized (this) { + if (!mCancelCallback) { + mWifiStateTrackerHandler.sendEmptyMessage(event); + } + } + } } if (powerMode != DRIVER_POWER_MODE_ACTIVE) { @@ -2484,17 +2576,15 @@ public class WifiStateTracker extends NetworkStateTracker { WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE); } - synchronized (this) { - if (!mCancelCallback) { - mTarget.sendEmptyMessage(event); - } - } break; } } public synchronized void setCancelCallback(boolean cancelCallback) { mCancelCallback = cancelCallback; + if (cancelCallback) { + mAlarmManager.cancel(mDhcpRenewalIntent); + } } /** @@ -2510,6 +2600,7 @@ public class WifiStateTracker extends NetworkStateTracker { int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset()); return state == BluetoothHeadset.STATE_DISCONNECTED; } + } private void checkUseStaticIp() { -- 2.11.0