diff options
| author | Jeff Sharkey <jsharkey@android.com> | 2012-04-19 23:01:08 -0700 |
|---|---|---|
| committer | Jeff Sharkey <jsharkey@android.com> | 2012-04-20 15:57:19 -0700 |
| commit | 9f6e4ba50e7e73704c7fbd3ba65fe73bdf8ad73f (patch) | |
| tree | ed8045c7cfb0fcea3c10b0f76d2808116ac1545e | |
| parent | f0c4c658488824707c206551fd1ccc0a0781031e (diff) | |
| download | frameworks_base-9f6e4ba50e7e73704c7fbd3ba65fe73bdf8ad73f.zip frameworks_base-9f6e4ba50e7e73704c7fbd3ba65fe73bdf8ad73f.tar.gz frameworks_base-9f6e4ba50e7e73704c7fbd3ba65fe73bdf8ad73f.tar.bz2 | |
Connect metered DHCP hint for Wi-Fi networks.
When DHCP lease includes vendor info indicating that remote Wi-Fi
network is metered, advise NetworkPolicy. Users can still manually
change the metered flag in Settings.
Also remove any policies belonging to removed Wi-Fi networks, and
teach isNetworkMetered() about ethernet networks.
Bug: 6344821, 6369307, 6365872
Change-Id: I108606c6fddf2d02828fcab011f3a1501415f1bc
| -rw-r--r-- | core/java/android/net/DhcpInfoInternal.java | 8 | ||||
| -rw-r--r-- | services/java/com/android/server/net/NetworkPolicyManagerService.java | 111 | ||||
| -rw-r--r-- | wifi/java/android/net/wifi/WifiInfo.java | 32 | ||||
| -rw-r--r-- | wifi/java/android/net/wifi/WifiStateMachine.java | 3 |
4 files changed, 149 insertions, 5 deletions
diff --git a/core/java/android/net/DhcpInfoInternal.java b/core/java/android/net/DhcpInfoInternal.java index 7ab8047..c87c34b 100644 --- a/core/java/android/net/DhcpInfoInternal.java +++ b/core/java/android/net/DhcpInfoInternal.java @@ -142,6 +142,14 @@ public class DhcpInfoInternal { } } + /** + * Test if this DHCP lease includes vendor hint that network link is + * metered, and sensitive to heavy data transfers. + */ + public boolean hasMeteredHint() { + return "ANDROID_METERED".equals(vendorInfo); + } + public String toString() { String routeString = ""; for (RouteInfo route : mRoutes) routeString += route.toString() + " | "; diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 1e17067..52d5019 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -30,6 +30,8 @@ import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIMAX; +import static android.net.ConnectivityManager.isNetworkTypeMobile; +import static android.net.NetworkPolicy.CYCLE_NONE; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; @@ -48,6 +50,14 @@ import static android.net.NetworkTemplate.MATCH_MOBILE_ALL; import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.TrafficStats.MB_IN_BYTES; +import static android.net.wifi.WifiInfo.removeDoubleQuotes; +import static android.net.wifi.WifiManager.CHANGE_REASON_ADDED; +import static android.net.wifi.WifiManager.CHANGE_REASON_REMOVED; +import static android.net.wifi.WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION; +import static android.net.wifi.WifiManager.EXTRA_CHANGE_REASON; +import static android.net.wifi.WifiManager.EXTRA_NETWORK_INFO; +import static android.net.wifi.WifiManager.EXTRA_WIFI_CONFIGURATION; +import static android.net.wifi.WifiManager.EXTRA_WIFI_INFO; import static android.telephony.TelephonyManager.SIM_STATE_READY; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static com.android.internal.util.ArrayUtils.appendInt; @@ -84,10 +94,14 @@ import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; import android.net.NetworkIdentity; +import android.net.NetworkInfo; import android.net.NetworkPolicy; import android.net.NetworkQuotaInfo; import android.net.NetworkState; import android.net.NetworkTemplate; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Environment; import android.os.Handler; @@ -355,6 +369,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext.registerReceiver(mSnoozeWarningReceiver, snoozeWarningFilter, MANAGE_NETWORK_POLICY, mHandler); + // listen for configured wifi networks to be removed + final IntentFilter wifiConfigFilter = new IntentFilter(CONFIGURED_NETWORKS_CHANGED_ACTION); + mContext.registerReceiver( + mWifiConfigReceiver, wifiConfigFilter, CONNECTIVITY_INTERNAL, mHandler); + + // listen for wifi state changes to catch metered hint + final IntentFilter wifiStateFilter = new IntentFilter( + WifiManager.NETWORK_STATE_CHANGED_ACTION); + mContext.registerReceiver( + mWifiStateReceiver, wifiStateFilter, CONNECTIVITY_INTERNAL, mHandler); + } private IProcessObserver mProcessObserver = new IProcessObserver.Stub() { @@ -463,6 +488,73 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { }; /** + * Receiver that watches for {@link WifiConfiguration} to be changed. + */ + private BroadcastReceiver mWifiConfigReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // on background handler thread, and verified CONNECTIVITY_INTERNAL + // permission above. + + final int reason = intent.getIntExtra(EXTRA_CHANGE_REASON, CHANGE_REASON_ADDED); + if (reason == CHANGE_REASON_REMOVED) { + final WifiConfiguration config = intent.getParcelableExtra( + EXTRA_WIFI_CONFIGURATION); + final NetworkTemplate template = NetworkTemplate.buildTemplateWifi( + removeDoubleQuotes(config.SSID)); + synchronized (mRulesLock) { + if (mNetworkPolicy.containsKey(template)) { + mNetworkPolicy.remove(template); + writePolicyLocked(); + } + } + } + } + }; + + /** + * Receiver that watches {@link WifiInfo} state changes to infer metered + * state. Ignores hints when policy is user-defined. + */ + private BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // on background handler thread, and verified CONNECTIVITY_INTERNAL + // permission above. + + // ignore when not connected + final NetworkInfo netInfo = intent.getParcelableExtra(EXTRA_NETWORK_INFO); + if (!netInfo.isConnected()) return; + + final WifiInfo info = intent.getParcelableExtra(EXTRA_WIFI_INFO); + final boolean meteredHint = info.getMeteredHint(); + + final NetworkTemplate template = NetworkTemplate.buildTemplateWifi( + removeDoubleQuotes(info.getSSID())); + synchronized (mRulesLock) { + NetworkPolicy policy = mNetworkPolicy.get(template); + if (policy == null && meteredHint) { + // policy doesn't exist, and AP is hinting that it's + // metered: create an inferred policy. + policy = new NetworkPolicy(template, CYCLE_NONE, Time.TIMEZONE_UTC, + WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, + meteredHint, true); + addNetworkPolicyLocked(policy); + + } else if (policy != null && policy.inferred) { + // policy exists, and was inferred: update its current + // metered state. + policy.metered = meteredHint; + + // since this is inferred for each wifi session, just update + // rules without persisting. + updateNetworkRulesLocked(); + } + } + } + }; + + /** * Observer that watches for {@link INetworkManagementService} alerts. */ private INetworkManagementEventObserver mAlertObserver = new NetworkAlertObserver() { @@ -974,9 +1066,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final String cycleTimezone = time.timezone; final NetworkTemplate template = buildTemplateMobileAll(subscriberId); - mNetworkPolicy.put(template, new NetworkPolicy(template, cycleDay, cycleTimezone, - warningBytes, LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, true, true)); - writePolicyLocked(); + final NetworkPolicy policy = new NetworkPolicy(template, cycleDay, cycleTimezone, + warningBytes, LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, true, true); + addNetworkPolicyLocked(policy); } } @@ -1269,6 +1361,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private void addNetworkPolicyLocked(NetworkPolicy policy) { + mNetworkPolicy.put(policy.template, policy); + + updateNetworkEnabledLocked(); + updateNetworkRulesLocked(); + updateNotificationsLocked(); + writePolicyLocked(); + } + @Override public NetworkPolicy[] getNetworkPolicies() { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); @@ -1402,6 +1503,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (policy != null) { return policy.metered; } else { + final int type = state.networkInfo.getType(); + if (isNetworkTypeMobile(type) || type == TYPE_WIMAX) { + return true; + } return false; } } diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 7bb927b..30e4a20 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -68,9 +68,14 @@ public class WifiInfo implements Parcelable { private int mLinkSpeed; private InetAddress mIpAddress; - private String mMacAddress; + /** + * Flag indicating that AP has hinted that upstream connection is metered, + * and sensitive to heavy data transfers. + */ + private boolean mMeteredHint; + WifiInfo() { mSSID = null; mBSSID = null; @@ -96,6 +101,7 @@ public class WifiInfo implements Parcelable { mLinkSpeed = source.mLinkSpeed; mIpAddress = source.mIpAddress; mMacAddress = source.mMacAddress; + mMeteredHint = source.mMeteredHint; } } @@ -168,6 +174,16 @@ public class WifiInfo implements Parcelable { return mMacAddress; } + /** {@hide} */ + public void setMeteredHint(boolean meteredHint) { + mMeteredHint = meteredHint; + } + + /** {@hide} */ + public boolean getMeteredHint() { + return mMeteredHint; + } + void setNetworkId(int id) { mNetworkId = id; } @@ -248,6 +264,15 @@ public class WifiInfo implements Parcelable { } } + /** {@hide} */ + public static String removeDoubleQuotes(String string) { + final int length = string.length(); + if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) { + return string.substring(1, length - 1); + } + return string; + } + @Override public String toString() { StringBuffer sb = new StringBuffer(); @@ -260,7 +285,8 @@ public class WifiInfo implements Parcelable { append(mSupplicantState == null ? none : mSupplicantState). append(", RSSI: ").append(mRssi). append(", Link speed: ").append(mLinkSpeed). - append(", Net ID: ").append(mNetworkId); + append(", Net ID: ").append(mNetworkId). + append(", Metered hint: ").append(mMeteredHint); return sb.toString(); } @@ -284,6 +310,7 @@ public class WifiInfo implements Parcelable { dest.writeString(getSSID()); dest.writeString(mBSSID); dest.writeString(mMacAddress); + dest.writeInt(mMeteredHint ? 1 : 0); mSupplicantState.writeToParcel(dest, flags); } @@ -303,6 +330,7 @@ public class WifiInfo implements Parcelable { info.setSSID(in.readString()); info.mBSSID = in.readString(); info.mMacAddress = in.readString(); + info.mMeteredHint = in.readInt() != 0; info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in); return info; } diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 2f14098..705e3c7 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -1642,6 +1642,7 @@ public class WifiStateMachine extends StateMachine { mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID); mWifiInfo.setRssi(MIN_RSSI); mWifiInfo.setLinkSpeed(-1); + mWifiInfo.setMeteredHint(false); setNetworkDetailedState(DetailedState.DISCONNECTED); mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED); @@ -1713,6 +1714,7 @@ public class WifiStateMachine extends StateMachine { mWifiConfigStore.setIpConfiguration(mLastNetworkId, dhcpInfoInternal); InetAddress addr = NetworkUtils.numericToInetAddress(dhcpInfoInternal.ipAddress); mWifiInfo.setInetAddress(addr); + mWifiInfo.setMeteredHint(dhcpInfoInternal.hasMeteredHint()); if (getNetworkDetailedState() == DetailedState.CONNECTED) { //DHCP renewal in connected state LinkProperties linkProperties = dhcpInfoInternal.makeLinkProperties(); @@ -1735,6 +1737,7 @@ public class WifiStateMachine extends StateMachine { loge("IP configuration failed"); mWifiInfo.setInetAddress(null); + mWifiInfo.setMeteredHint(false); /** * If we've exceeded the maximum number of retries for DHCP * to a given network, disable the network |
