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
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index af7a465..b6c5c6f 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -119,12 +119,9 @@
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 @@
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 @@
mReason = source.mReason;
mExtraInfo = source.mExtraInfo;
mIsFailover = source.mIsFailover;
- mIsRoaming = source.mIsRoaming;
mIsAvailable = source.mIsAvailable;
+ mIsRoaming = source.mIsRoaming;
+ mIsMetered = source.mIsMetered;
}
}
}
@@ -330,6 +326,30 @@
}
/**
+ * 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 @@
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 @@
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<NetworkInfo> CREATOR =
- new Creator<NetworkInfo>() {
- 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<NetworkInfo> CREATOR = new Creator<NetworkInfo>() {
+ @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 933287f..95e3802 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -25,6 +25,7 @@
* @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 b069361..f06583b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -821,37 +821,25 @@
}
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 @@
synchronized (mNetworkForNetId) {
return mNetworkForNetId.get(network.netId);
}
- };
+ }
private Network[] getVpnUnderlyingNetworks(int uid) {
if (!mLockdownEnabled) {
@@ -877,12 +865,6 @@
}
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 @@
}
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 @@
}
/**
- * 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);
}
- return info;
+
+ // 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);
+ }
}
/**
@@ -980,10 +962,10 @@
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 @@
@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 @@
// 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 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 @@
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 @@
@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 @@
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 c5d38cb..3201060 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.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 @@
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() + "} " +