From 40d1fb87a4c29784d56610a8d67a16113113fa99 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 22 Apr 2016 09:50:16 -0600 Subject: [PATCH] API changes for DownloadManager and JobScheduler. To support moving DownloadManager, add new JobScheduler network type constraint that matches "any network except roaming." Also add an API to get a specific JobInfo by ID. Since the default network can be different on a per-app basis, and individual apps may be blocked due to app standby, evaluate job connectivity constraints on a per-UID basis. To implement this cleanly, add NetworkInfo.isMetered() to match the isRoaming() API. Add new DownloadManager APIs to support charging and device idle constraints, which are plumbed through to JobScheduler under the hood when scheduled. Add filtering to JobScheduler dumpsys to omit noisy details for packages the caller isn't interested in. Bug: 28098882, 26571724, 19821935 Change-Id: I09ca7184ef7ce6adba399f579d415a5fb2ea6110 --- core/java/android/net/NetworkInfo.java | 100 +++++++----- core/java/android/net/NetworkState.java | 1 + .../android/server/ConnectivityService.java | 152 +++++++----------- .../server/connectivity/NetworkAgentInfo.java | 12 ++ 4 files changed, 128 insertions(+), 137 deletions(-) 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() + "} " +