diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index af7a465880..b6c5c6fa96 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -119,12 +119,9 @@ public class NetworkInfo implements Parcelable { private String mReason; private String mExtraInfo; private boolean mIsFailover; - private boolean mIsRoaming; - - /** - * Indicates whether network connectivity is possible: - */ private boolean mIsAvailable; + private boolean mIsRoaming; + private boolean mIsMetered; /** * @hide @@ -139,8 +136,6 @@ public class NetworkInfo implements Parcelable { mSubtypeName = subtypeName; setDetailedState(DetailedState.IDLE, null, null); mState = State.UNKNOWN; - mIsAvailable = false; // until we're told otherwise, assume unavailable - mIsRoaming = false; } /** {@hide} */ @@ -156,8 +151,9 @@ public class NetworkInfo implements Parcelable { mReason = source.mReason; mExtraInfo = source.mExtraInfo; mIsFailover = source.mIsFailover; - mIsRoaming = source.mIsRoaming; mIsAvailable = source.mIsAvailable; + mIsRoaming = source.mIsRoaming; + mIsMetered = source.mIsMetered; } } } @@ -329,6 +325,30 @@ public class NetworkInfo implements Parcelable { } } + /** + * Returns if this network is metered. A network is classified as metered + * when the user is sensitive to heavy data usage on that connection due to + * monetary costs, data limitations or battery/performance issues. You + * should check this before doing large data transfers, and warn the user or + * delay the operation until another network is available. + * + * @return {@code true} if large transfers should be avoided, otherwise + * {@code false}. + */ + public boolean isMetered() { + synchronized (this) { + return mIsMetered; + } + } + + /** {@hide} */ + @VisibleForTesting + public void setMetered(boolean isMetered) { + synchronized (this) { + mIsMetered = isMetered; + } + } + /** * Reports the current coarse-grained state of the network. * @return the coarse-grained state @@ -409,26 +429,21 @@ public class NetworkInfo implements Parcelable { append("], state: ").append(mState).append("/").append(mDetailedState). append(", reason: ").append(mReason == null ? "(unspecified)" : mReason). append(", extra: ").append(mExtraInfo == null ? "(none)" : mExtraInfo). - append(", roaming: ").append(mIsRoaming). append(", failover: ").append(mIsFailover). - append(", isAvailable: ").append(mIsAvailable). + append(", available: ").append(mIsAvailable). + append(", roaming: ").append(mIsRoaming). + append(", metered: ").append(mIsMetered). append("]"); return builder.toString(); } } - /** - * Implement the Parcelable interface - * @hide - */ + @Override public int describeContents() { return 0; } - /** - * Implement the Parcelable interface. - * @hide - */ + @Override public void writeToParcel(Parcel dest, int flags) { synchronized (this) { dest.writeInt(mNetworkType); @@ -440,35 +455,34 @@ public class NetworkInfo implements Parcelable { dest.writeInt(mIsFailover ? 1 : 0); dest.writeInt(mIsAvailable ? 1 : 0); dest.writeInt(mIsRoaming ? 1 : 0); + dest.writeInt(mIsMetered ? 1 : 0); dest.writeString(mReason); dest.writeString(mExtraInfo); } } - /** - * Implement the Parcelable interface. - * @hide - */ - public static final Creator CREATOR = - new Creator() { - public NetworkInfo createFromParcel(Parcel in) { - int netType = in.readInt(); - int subtype = in.readInt(); - String typeName = in.readString(); - String subtypeName = in.readString(); - NetworkInfo netInfo = new NetworkInfo(netType, subtype, typeName, subtypeName); - netInfo.mState = State.valueOf(in.readString()); - netInfo.mDetailedState = DetailedState.valueOf(in.readString()); - netInfo.mIsFailover = in.readInt() != 0; - netInfo.mIsAvailable = in.readInt() != 0; - netInfo.mIsRoaming = in.readInt() != 0; - netInfo.mReason = in.readString(); - netInfo.mExtraInfo = in.readString(); - return netInfo; - } + public static final Creator CREATOR = new Creator() { + @Override + public NetworkInfo createFromParcel(Parcel in) { + int netType = in.readInt(); + int subtype = in.readInt(); + String typeName = in.readString(); + String subtypeName = in.readString(); + NetworkInfo netInfo = new NetworkInfo(netType, subtype, typeName, subtypeName); + netInfo.mState = State.valueOf(in.readString()); + netInfo.mDetailedState = DetailedState.valueOf(in.readString()); + netInfo.mIsFailover = in.readInt() != 0; + netInfo.mIsAvailable = in.readInt() != 0; + netInfo.mIsRoaming = in.readInt() != 0; + netInfo.mIsMetered = in.readInt() != 0; + netInfo.mReason = in.readString(); + netInfo.mExtraInfo = in.readString(); + return netInfo; + } - public NetworkInfo[] newArray(int size) { - return new NetworkInfo[size]; - } - }; + @Override + public NetworkInfo[] newArray(int size) { + return new NetworkInfo[size]; + } + }; } diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java index 933287f634..95e3802eee 100644 --- a/core/java/android/net/NetworkState.java +++ b/core/java/android/net/NetworkState.java @@ -25,6 +25,7 @@ import android.os.Parcelable; * @hide */ public class NetworkState implements Parcelable { + public static final NetworkState EMPTY = new NetworkState(null, null, null, null, null, null); public final NetworkInfo networkInfo; public final LinkProperties linkProperties; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index b06936129b..f06583bdf1 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -821,37 +821,25 @@ public class ConnectivityService extends IConnectivityManager.Stub } private NetworkState getFilteredNetworkState(int networkType, int uid) { - NetworkInfo info = null; - LinkProperties lp = null; - NetworkCapabilities nc = null; - Network network = null; - String subscriberId = null; - if (mLegacyTypeTracker.isTypeSupported(networkType)) { - NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); + final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); + final NetworkState state; if (nai != null) { - synchronized (nai) { - info = new NetworkInfo(nai.networkInfo); - lp = new LinkProperties(nai.linkProperties); - nc = new NetworkCapabilities(nai.networkCapabilities); - // Network objects are outwardly immutable so there is no point to duplicating. - // Duplicating also precludes sharing socket factories and connection pools. - network = nai.network; - subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null; - } - info.setType(networkType); + state = nai.getNetworkState(); + state.networkInfo.setType(networkType); } else { - info = new NetworkInfo(networkType, 0, getNetworkTypeName(networkType), ""); + final NetworkInfo info = new NetworkInfo(networkType, 0, + getNetworkTypeName(networkType), ""); info.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null); info.setIsAvailable(true); - lp = new LinkProperties(); - nc = new NetworkCapabilities(); - network = null; + state = new NetworkState(info, new LinkProperties(), new NetworkCapabilities(), + null, null, null); } - info = getFilteredNetworkInfo(info, lp, uid); + filterNetworkStateForUid(state, uid); + return state; + } else { + return NetworkState.EMPTY; } - - return new NetworkState(info, lp, nc, network, subscriberId, null); } private NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) { @@ -861,7 +849,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mNetworkForNetId) { return mNetworkForNetId.get(network.netId); } - }; + } private Network[] getVpnUnderlyingNetworks(int uid) { if (!mLockdownEnabled) { @@ -877,12 +865,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } private NetworkState getUnfilteredActiveNetworkState(int uid) { - NetworkInfo info = null; - LinkProperties lp = null; - NetworkCapabilities nc = null; - Network network = null; - String subscriberId = null; - NetworkAgentInfo nai = getDefaultNetwork(); final Network[] networks = getVpnUnderlyingNetworks(uid); @@ -900,18 +882,10 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (nai != null) { - synchronized (nai) { - info = new NetworkInfo(nai.networkInfo); - lp = new LinkProperties(nai.linkProperties); - nc = new NetworkCapabilities(nai.networkCapabilities); - // Network objects are outwardly immutable so there is no point to duplicating. - // Duplicating also precludes sharing socket factories and connection pools. - network = nai.network; - subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null; - } + return nai.getNetworkState(); + } else { + return NetworkState.EMPTY; } - - return new NetworkState(info, lp, nc, network, subscriberId, null); } /** @@ -952,21 +926,29 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** - * Return a filtered {@link NetworkInfo}, potentially marked - * {@link DetailedState#BLOCKED} based on - * {@link #isNetworkWithLinkPropertiesBlocked}. + * Apply any relevant filters to {@link NetworkState} for the given UID. For + * example, this may mark the network as {@link DetailedState#BLOCKED} based + * on {@link #isNetworkWithLinkPropertiesBlocked}, or + * {@link NetworkInfo#isMetered()} based on network policies. */ - private NetworkInfo getFilteredNetworkInfo(NetworkInfo info, LinkProperties lp, int uid) { - if (info != null && isNetworkWithLinkPropertiesBlocked(lp, uid)) { - // network is blocked; clone and override state - info = new NetworkInfo(info); - info.setDetailedState(DetailedState.BLOCKED, null, null); + private void filterNetworkStateForUid(NetworkState state, int uid) { + if (state == null || state.networkInfo == null || state.linkProperties == null) return; + + if (isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid)) { + state.networkInfo.setDetailedState(DetailedState.BLOCKED, null, null); } - if (info != null && mLockdownTracker != null) { - info = mLockdownTracker.augmentNetworkInfo(info); - if (VDBG) log("returning Locked NetworkInfo"); + if (mLockdownTracker != null) { + mLockdownTracker.augmentNetworkInfo(state.networkInfo); + } + + // TODO: apply metered state closer to NetworkAgentInfo + final long token = Binder.clearCallingIdentity(); + try { + state.networkInfo.setMetered(mPolicyManager.isNetworkMetered(state)); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(token); } - return info; } /** @@ -980,10 +962,10 @@ public class ConnectivityService extends IConnectivityManager.Stub public NetworkInfo getActiveNetworkInfo() { enforceAccessPermission(); final int uid = Binder.getCallingUid(); - NetworkState state = getUnfilteredActiveNetworkState(uid); - NetworkInfo ni = getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid); - maybeLogBlockedNetworkInfo(ni, uid); - return ni; + final NetworkState state = getUnfilteredActiveNetworkState(uid); + filterNetworkStateForUid(state, uid); + maybeLogBlockedNetworkInfo(state.networkInfo, uid); + return state.networkInfo; } @Override @@ -1027,8 +1009,9 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkInfo getActiveNetworkInfoForUid(int uid) { enforceConnectivityInternalPermission(); - NetworkState state = getUnfilteredActiveNetworkState(uid); - return getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid); + final NetworkState state = getUnfilteredActiveNetworkState(uid); + filterNetworkStateForUid(state, uid); + return state.networkInfo; } @Override @@ -1039,12 +1022,13 @@ public class ConnectivityService extends IConnectivityManager.Stub // A VPN is active, so we may need to return one of its underlying networks. This // information is not available in LegacyTypeTracker, so we have to get it from // getUnfilteredActiveNetworkState. - NetworkState state = getUnfilteredActiveNetworkState(uid); + final NetworkState state = getUnfilteredActiveNetworkState(uid); if (state.networkInfo != null && state.networkInfo.getType() == networkType) { - return getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid); + filterNetworkStateForUid(state, uid); + return state.networkInfo; } } - NetworkState state = getFilteredNetworkState(networkType, uid); + final NetworkState state = getFilteredNetworkState(networkType, uid); return state.networkInfo; } @@ -1052,15 +1036,14 @@ public class ConnectivityService extends IConnectivityManager.Stub public NetworkInfo getNetworkInfoForNetwork(Network network) { enforceAccessPermission(); final int uid = Binder.getCallingUid(); - NetworkInfo info = null; - NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); + final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); if (nai != null) { - synchronized (nai) { - info = new NetworkInfo(nai.networkInfo); - info = getFilteredNetworkInfo(info, nai.linkProperties, uid); - } + final NetworkState state = nai.getNetworkState(); + filterNetworkStateForUid(state, uid); + return state.networkInfo; + } else { + return null; } - return info; } @Override @@ -1222,12 +1205,7 @@ public class ConnectivityService extends IConnectivityManager.Stub for (Network network : getAllNetworks()) { final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); if (nai != null) { - synchronized (nai) { - final String subscriberId = (nai.networkMisc != null) - ? nai.networkMisc.subscriberId : null; - result.add(new NetworkState(nai.networkInfo, nai.linkProperties, - nai.networkCapabilities, network, subscriberId, null)); - } + result.add(nai.getNetworkState()); } } return result.toArray(new NetworkState[result.size()]); @@ -1255,24 +1233,9 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public boolean isActiveNetworkMetered() { enforceAccessPermission(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - return isActiveNetworkMeteredUnchecked(uid); - } finally { - Binder.restoreCallingIdentity(token); - } - } - private boolean isActiveNetworkMeteredUnchecked(int uid) { - final NetworkState state = getUnfilteredActiveNetworkState(uid); - if (state.networkInfo != null) { - try { - return mPolicyManager.isNetworkMetered(state); - } catch (RemoteException e) { - } - } - return false; + final NetworkInfo info = getActiveNetworkInfo(); + return (info != null) ? info.isMetered() : false; } private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() { @@ -1490,7 +1453,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private Intent makeGeneralIntent(NetworkInfo info, String bcastType) { if (mLockdownTracker != null) { - info = mLockdownTracker.augmentNetworkInfo(info); + info = new NetworkInfo(info); + mLockdownTracker.augmentNetworkInfo(info); } Intent intent = new Intent(bcastType); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index c5d38cb73d..32010608d4 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -25,6 +25,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkMisc; import android.net.NetworkRequest; +import android.net.NetworkState; import android.os.Handler; import android.os.Messenger; import android.util.SparseArray; @@ -247,6 +248,17 @@ public class NetworkAgentInfo implements Comparable { currentScore = newScore; } + public NetworkState getNetworkState() { + synchronized (this) { + // Network objects are outwardly immutable so there is no point to duplicating. + // Duplicating also precludes sharing socket factories and connection pools. + final String subscriberId = (networkMisc != null) ? networkMisc.subscriberId : null; + return new NetworkState(new NetworkInfo(networkInfo), + new LinkProperties(linkProperties), + new NetworkCapabilities(networkCapabilities), network, subscriberId, null); + } + } + public String toString() { return "NetworkAgentInfo{ ni{" + networkInfo + "} " + "network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " +