Merge "Connect metered DHCP hint for Wi-Fi networks."
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 @@
}
}
+ /**
+ * 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_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_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.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 @@
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 @@
};
/**
+ * 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 @@
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 @@
}
}
+ 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 @@
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 @@
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 @@
mLinkSpeed = source.mLinkSpeed;
mIpAddress = source.mIpAddress;
mMacAddress = source.mMacAddress;
+ mMeteredHint = source.mMeteredHint;
}
}
@@ -168,6 +174,16 @@
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 @@
}
}
+ /** {@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 @@
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 @@
dest.writeString(getSSID());
dest.writeString(mBSSID);
dest.writeString(mMacAddress);
+ dest.writeInt(mMeteredHint ? 1 : 0);
mSupplicantState.writeToParcel(dest, flags);
}
@@ -303,6 +330,7 @@
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 @@
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 @@
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 @@
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