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
This commit is contained in:
Jeff Sharkey
2016-04-22 09:50:16 -06:00
parent 134a18cdea
commit 40d1fb87a4
4 changed files with 128 additions and 137 deletions

View File

@@ -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<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];
}
};
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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<NetworkAgentInfo> {
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() + "} " +