From f07c7b9fd0a640bff4bf7690373613da217fe69b 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 --- api/current.txt | 6 + api/system-current.txt | 6 + api/test-current.txt | 6 + core/java/android/app/DownloadManager.java | 68 +++++-- core/java/android/app/JobSchedulerImpl.java | 9 + core/java/android/app/job/IJobScheduler.aidl | 1 + core/java/android/app/job/JobInfo.java | 7 +- core/java/android/app/job/JobScheduler.java | 18 +- core/java/android/net/NetworkInfo.java | 100 ++++++----- core/java/android/net/NetworkState.java | 1 + core/java/android/provider/Downloads.java | 5 + .../android/server/ConnectivityService.java | 152 ++++++---------- .../server/connectivity/NetworkAgentInfo.java | 12 ++ .../server/job/JobSchedulerService.java | 50 +++++- .../java/com/android/server/job/JobStore.java | 17 +- .../controllers/ConnectivityController.java | 168 +++++++++--------- .../server/job/controllers/JobStatus.java | 26 ++- .../server/net/LockdownVpnTracker.java | 8 +- .../net/NetworkPolicyManagerService.java | 8 +- 19 files changed, 408 insertions(+), 260 deletions(-) diff --git a/api/current.txt b/api/current.txt index 15fc14909231a..9834c8cd08554 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4359,6 +4359,8 @@ package android.app { method public android.app.DownloadManager.Request setDestinationUri(android.net.Uri); method public android.app.DownloadManager.Request setMimeType(java.lang.String); method public android.app.DownloadManager.Request setNotificationVisibility(int); + method public android.app.DownloadManager.Request setRequiresCharging(boolean); + method public android.app.DownloadManager.Request setRequiresDeviceIdle(boolean); method public deprecated android.app.DownloadManager.Request setShowRunningNotification(boolean); method public android.app.DownloadManager.Request setTitle(java.lang.CharSequence); method public android.app.DownloadManager.Request setVisibleInDownloadsUi(boolean); @@ -6335,6 +6337,7 @@ package android.app.job { field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L field public static final int NETWORK_TYPE_ANY = 1; // 0x1 field public static final int NETWORK_TYPE_NONE = 0; // 0x0 + field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3 field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2 } @@ -6382,6 +6385,7 @@ package android.app.job { method public abstract void cancel(int); method public abstract void cancelAll(); method public abstract java.util.List getAllPendingJobs(); + method public abstract android.app.job.JobInfo getPendingJob(int); method public abstract int schedule(android.app.job.JobInfo); field public static final int RESULT_FAILURE = 0; // 0x0 field public static final int RESULT_SUCCESS = 1; // 0x1 @@ -23757,8 +23761,10 @@ package android.net { method public boolean isConnected(); method public boolean isConnectedOrConnecting(); method public boolean isFailover(); + method public boolean isMetered(); method public boolean isRoaming(); method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; } public static final class NetworkInfo.DetailedState extends java.lang.Enum { diff --git a/api/system-current.txt b/api/system-current.txt index c489435bc0930..57497ad571e9a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4502,6 +4502,8 @@ package android.app { method public android.app.DownloadManager.Request setDestinationUri(android.net.Uri); method public android.app.DownloadManager.Request setMimeType(java.lang.String); method public android.app.DownloadManager.Request setNotificationVisibility(int); + method public android.app.DownloadManager.Request setRequiresCharging(boolean); + method public android.app.DownloadManager.Request setRequiresDeviceIdle(boolean); method public deprecated android.app.DownloadManager.Request setShowRunningNotification(boolean); method public android.app.DownloadManager.Request setTitle(java.lang.CharSequence); method public android.app.DownloadManager.Request setVisibleInDownloadsUi(boolean); @@ -6613,6 +6615,7 @@ package android.app.job { field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L field public static final int NETWORK_TYPE_ANY = 1; // 0x1 field public static final int NETWORK_TYPE_NONE = 0; // 0x0 + field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3 field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2 } @@ -6660,6 +6663,7 @@ package android.app.job { method public abstract void cancel(int); method public abstract void cancelAll(); method public abstract java.util.List getAllPendingJobs(); + method public abstract android.app.job.JobInfo getPendingJob(int); method public abstract int schedule(android.app.job.JobInfo); field public static final int RESULT_FAILURE = 0; // 0x0 field public static final int RESULT_SUCCESS = 1; // 0x1 @@ -25583,8 +25587,10 @@ package android.net { method public boolean isConnected(); method public boolean isConnectedOrConnecting(); method public boolean isFailover(); + method public boolean isMetered(); method public boolean isRoaming(); method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; } public static final class NetworkInfo.DetailedState extends java.lang.Enum { diff --git a/api/test-current.txt b/api/test-current.txt index f6340b205d60d..5dc6d51213c02 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -4359,6 +4359,8 @@ package android.app { method public android.app.DownloadManager.Request setDestinationUri(android.net.Uri); method public android.app.DownloadManager.Request setMimeType(java.lang.String); method public android.app.DownloadManager.Request setNotificationVisibility(int); + method public android.app.DownloadManager.Request setRequiresCharging(boolean); + method public android.app.DownloadManager.Request setRequiresDeviceIdle(boolean); method public deprecated android.app.DownloadManager.Request setShowRunningNotification(boolean); method public android.app.DownloadManager.Request setTitle(java.lang.CharSequence); method public android.app.DownloadManager.Request setVisibleInDownloadsUi(boolean); @@ -6339,6 +6341,7 @@ package android.app.job { field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L field public static final int NETWORK_TYPE_ANY = 1; // 0x1 field public static final int NETWORK_TYPE_NONE = 0; // 0x0 + field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3 field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2 } @@ -6386,6 +6389,7 @@ package android.app.job { method public abstract void cancel(int); method public abstract void cancelAll(); method public abstract java.util.List getAllPendingJobs(); + method public abstract android.app.job.JobInfo getPendingJob(int); method public abstract int schedule(android.app.job.JobInfo); field public static final int RESULT_FAILURE = 0; // 0x0 field public static final int RESULT_SUCCESS = 1; // 0x1 @@ -23825,8 +23829,10 @@ package android.net { method public boolean isConnected(); method public boolean isConnectedOrConnecting(); method public boolean isFailover(); + method public boolean isMetered(); method public boolean isRoaming(); method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; } public static final class NetworkInfo.DetailedState extends java.lang.Enum { diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index e681d474d9c1d..29bc4d804f6bf 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -370,6 +370,7 @@ public class DownloadManager { * {@link ConnectivityManager#TYPE_BLUETOOTH}. * @hide */ + @Deprecated public static final int NETWORK_BLUETOOTH = 1 << 2; private Uri mUri; @@ -381,6 +382,7 @@ public class DownloadManager { private int mAllowedNetworkTypes = ~0; // default to all network types allowed private boolean mRoamingAllowed = true; private boolean mMeteredAllowed = true; + private int mFlags = 0; private boolean mIsVisibleInDownloadsUi = true; private boolean mScannable = false; private boolean mUseSystemCache = false; @@ -669,6 +671,10 @@ public class DownloadManager { * By default, all network types are allowed. Consider using * {@link #setAllowedOverMetered(boolean)} instead, since it's more * flexible. + *

+ * As of {@link android.os.Build.VERSION_CODES#N}, setting only the + * {@link #NETWORK_WIFI} flag here is equivalent to calling + * {@link #setAllowedOverMetered(boolean)} with {@code false}. * * @param flags any combination of the NETWORK_* bit flags. * @return this object @@ -700,6 +706,42 @@ public class DownloadManager { return this; } + /** + * Specify that to run this download, the device needs to be plugged in. + * This defaults to false. + * + * @param requiresCharging Whether or not the device is plugged in. + * @see android.app.job.JobInfo.Builder#setRequiresCharging(boolean) + */ + public Request setRequiresCharging(boolean requiresCharging) { + if (requiresCharging) { + mFlags |= Downloads.Impl.FLAG_REQUIRES_CHARGING; + } else { + mFlags &= ~Downloads.Impl.FLAG_REQUIRES_CHARGING; + } + return this; + } + + /** + * Specify that to run, the download needs the device to be in idle + * mode. This defaults to false. + *

+ * Idle mode is a loose definition provided by the system, which means + * that the device is not in use, and has not been in use for some time. + * + * @param requiresDeviceIdle Whether or not the device need be within an + * idle maintenance window. + * @see android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean) + */ + public Request setRequiresDeviceIdle(boolean requiresDeviceIdle) { + if (requiresDeviceIdle) { + mFlags |= Downloads.Impl.FLAG_REQUIRES_DEVICE_IDLE; + } else { + mFlags &= ~Downloads.Impl.FLAG_REQUIRES_DEVICE_IDLE; + } + return this; + } + /** * Set whether this download should be displayed in the system's Downloads UI. True by * default. @@ -746,6 +788,7 @@ public class DownloadManager { values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, mAllowedNetworkTypes); values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed); values.put(Downloads.Impl.COLUMN_ALLOW_METERED, mMeteredAllowed); + values.put(Downloads.Impl.COLUMN_FLAGS, mFlags); values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, mIsVisibleInDownloadsUi); return values; @@ -983,16 +1026,7 @@ public class DownloadManager { // called with nothing to remove! throw new IllegalArgumentException("input param 'ids' can't be null"); } - ContentValues values = new ContentValues(); - values.put(Downloads.Impl.COLUMN_DELETED, 1); - // if only one id is passed in, then include it in the uri itself. - // this will eliminate a full database scan in the download service. - if (ids.length == 1) { - return mResolver.update(ContentUris.withAppendedId(mBaseUri, ids[0]), values, - null, null); - } - return mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), - getWhereArgsForIds(ids)); + return mResolver.delete(mBaseUri, getWhereClauseForIds(ids), getWhereArgsForIds(ids)); } /** @@ -1120,6 +1154,20 @@ public class DownloadManager { mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), getWhereArgsForIds(ids)); } + /** + * Force the given downloads to proceed even if their size is larger than + * {@link #getMaxBytesOverMobile(Context)}. + * + * @hide + */ + public void forceDownload(long... ids) { + ContentValues values = new ContentValues(); + values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING); + values.put(Downloads.Impl.COLUMN_CONTROL, Downloads.Impl.CONTROL_RUN); + values.put(Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT, 1); + mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), getWhereArgsForIds(ids)); + } + /** * Returns maximum size, in bytes, of downloads that may go over a mobile connection; or null if * there's no limit diff --git a/core/java/android/app/JobSchedulerImpl.java b/core/java/android/app/JobSchedulerImpl.java index b3a486f450bfe..e30b96fd692a9 100644 --- a/core/java/android/app/JobSchedulerImpl.java +++ b/core/java/android/app/JobSchedulerImpl.java @@ -78,4 +78,13 @@ public class JobSchedulerImpl extends JobScheduler { return null; } } + + @Override + public JobInfo getPendingJob(int jobId) { + try { + return mBinder.getPendingJob(jobId); + } catch (RemoteException e) { + return null; + } + } } diff --git a/core/java/android/app/job/IJobScheduler.aidl b/core/java/android/app/job/IJobScheduler.aidl index 3379f2ec7c9cf..b6eec27c5412a 100644 --- a/core/java/android/app/job/IJobScheduler.aidl +++ b/core/java/android/app/job/IJobScheduler.aidl @@ -28,4 +28,5 @@ interface IJobScheduler { void cancel(int jobId); void cancelAll(); List getAllPendingJobs(); + JobInfo getPendingJob(int jobId); } diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index 602d9508991ef..ecfc527cb7f36 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -44,6 +44,8 @@ public class JobInfo implements Parcelable { public static final int NETWORK_TYPE_ANY = 1; /** This job requires network connectivity that is unmetered. */ public static final int NETWORK_TYPE_UNMETERED = 2; + /** This job requires network connectivity that is not roaming. */ + public static final int NETWORK_TYPE_NOT_ROAMING = 3; /** * Amount of backoff a job has initially by default, in milliseconds. @@ -240,8 +242,9 @@ public class JobInfo implements Parcelable { /** * One of {@link android.app.job.JobInfo#NETWORK_TYPE_ANY}, - * {@link android.app.job.JobInfo#NETWORK_TYPE_NONE}, or - * {@link android.app.job.JobInfo#NETWORK_TYPE_UNMETERED}. + * {@link android.app.job.JobInfo#NETWORK_TYPE_NONE}, + * {@link android.app.job.JobInfo#NETWORK_TYPE_UNMETERED}, or + * {@link android.app.job.JobInfo#NETWORK_TYPE_NOT_ROAMING}. */ public int getNetworkType() { return networkType; diff --git a/core/java/android/app/job/JobScheduler.java b/core/java/android/app/job/JobScheduler.java index d1e563f90f2d8..9618cd10c0865 100644 --- a/core/java/android/app/job/JobScheduler.java +++ b/core/java/android/app/job/JobScheduler.java @@ -16,6 +16,9 @@ package android.app.job; +import android.annotation.NonNull; +import android.annotation.Nullable; + import java.util.List; /** @@ -78,7 +81,6 @@ public abstract class JobScheduler { * Cancel a job that is pending in the JobScheduler. * @param jobId unique identifier for this job. Obtain this value from the jobs returned by * {@link #getAllPendingJobs()}. - * @return */ public abstract void cancel(int jobId); @@ -88,8 +90,18 @@ public abstract class JobScheduler { public abstract void cancelAll(); /** - * @return a list of all the jobs registered by this package that have not yet been executed. + * Retrieve all jobs for this package that are pending in the JobScheduler. + * + * @return a list of all the jobs registered by this package that have not + * yet been executed. */ - public abstract List getAllPendingJobs(); + public abstract @NonNull List getAllPendingJobs(); + /** + * Retrieve a specific job for this package that is pending in the + * JobScheduler. + * + * @return job registered by this package that has not yet been executed. + */ + public abstract @Nullable JobInfo getPendingJob(int jobId); } diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index af7a465880893..b6c5c6fa966f8 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 933287f6341f4..95e3802eeefa1 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/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index 8472f78582608..b826584009c18 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -412,6 +412,11 @@ public final class Downloads { public static final String COLUMN_ALLOW_WRITE = "allow_write"; + public static final int FLAG_REQUIRES_CHARGING = 1 << 0; + public static final int FLAG_REQUIRES_DEVICE_IDLE = 1 << 1; + + public static final String COLUMN_FLAGS = "flags"; + /** * default value for {@link #COLUMN_LAST_UPDATESRC}. * This value is used when this column's value is not relevant. diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index b06936129b75f..f06583bdf13cc 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 c5d38cb73d043..32010608d4396 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() + "} " + diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 7df8ffd7c8221..075a88f34ac96 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -40,6 +40,7 @@ import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; +import android.content.pm.PackageManager.NameNotFoundException; import android.os.BatteryStats; import android.os.Binder; import android.os.Handler; @@ -336,6 +337,19 @@ public final class JobSchedulerService extends com.android.server.SystemService } } + public JobInfo getPendingJob(int uid, int jobId) { + synchronized (mLock) { + List jobs = mJobs.getJobsByUid(uid); + for (int i = jobs.size() - 1; i >= 0; i--) { + JobStatus job = jobs.get(i); + if (job.getJobId() == jobId) { + return job.getJob(); + } + } + return null; + } + } + void cancelJobsForUser(int userHandle) { List jobsForUser; synchronized (mLock) { @@ -912,7 +926,8 @@ public final class JobSchedulerService extends com.android.server.SystemService if (job.hasIdleConstraint()) { idleCount++; } - if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) { + if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint() + || job.hasNotRoamingConstraint()) { connectivityCount++; } if (job.hasChargingConstraint()) { @@ -1345,6 +1360,18 @@ public final class JobSchedulerService extends com.android.server.SystemService } } + @Override + public JobInfo getPendingJob(int jobId) throws RemoteException { + final int uid = Binder.getCallingUid(); + + long ident = Binder.clearCallingIdentity(); + try { + return JobSchedulerService.this.getPendingJob(uid, jobId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + @Override public void cancelAll() throws RemoteException { final int uid = Binder.getCallingUid(); @@ -1378,7 +1405,7 @@ public final class JobSchedulerService extends com.android.server.SystemService long identityToken = Binder.clearCallingIdentity(); try { - JobSchedulerService.this.dumpInternal(pw); + JobSchedulerService.this.dumpInternal(pw, args); } finally { Binder.restoreCallingIdentity(identityToken); } @@ -1450,7 +1477,17 @@ public final class JobSchedulerService extends com.android.server.SystemService return s.toString(); } - void dumpInternal(final PrintWriter pw) { + void dumpInternal(final PrintWriter pw, String[] args) { + int filterUid = -1; + if (!ArrayUtils.isEmpty(args)) { + try { + filterUid = getContext().getPackageManager().getPackageUid(args[0], + PackageManager.MATCH_UNINSTALLED_PACKAGES); + } catch (NameNotFoundException ignored) { + } + } + + final int filterUidFinal = filterUid; final long now = SystemClock.elapsedRealtime(); synchronized (mLock) { pw.println("Started users: " + Arrays.toString(mStartedUsers)); @@ -1463,6 +1500,13 @@ public final class JobSchedulerService extends com.android.server.SystemService public void process(JobStatus job) { pw.print(" Job #"); pw.print(index++); pw.print(": "); pw.println(job.toShortString()); + + // Skip printing details if the caller requested a filter + if (filterUidFinal != -1 && job.getUid() != filterUidFinal + && job.getSourceUid() != filterUidFinal) { + return; + } + job.dump(pw, " ", true); pw.print(" Ready: "); pw.print(mHandler.isReadyToBeExecutedLocked(job)); diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java index 35628a2e6fd18..5ad988a42792d 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -365,11 +365,14 @@ public class JobStore { */ private void writeConstraintsToXml(XmlSerializer out, JobStatus jobStatus) throws IOException { out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS); + if (jobStatus.hasConnectivityConstraint()) { + out.attribute(null, "connectivity", Boolean.toString(true)); + } if (jobStatus.hasUnmeteredConstraint()) { out.attribute(null, "unmetered", Boolean.toString(true)); } - if (jobStatus.hasConnectivityConstraint()) { - out.attribute(null, "connectivity", Boolean.toString(true)); + if (jobStatus.hasNotRoamingConstraint()) { + out.attribute(null, "not-roaming", Boolean.toString(true)); } if (jobStatus.hasIdleConstraint()) { out.attribute(null, "idle", Boolean.toString(true)); @@ -693,13 +696,17 @@ public class JobStore { } private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) { - String val = parser.getAttributeValue(null, "unmetered"); + String val = parser.getAttributeValue(null, "connectivity"); + if (val != null) { + jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); + } + val = parser.getAttributeValue(null, "unmetered"); if (val != null) { jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); } - val = parser.getAttributeValue(null, "connectivity"); + val = parser.getAttributeValue(null, "not-roaming"); if (val != null) { - jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); + jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING); } val = parser.getAttributeValue(null, "idle"); if (val != null) { diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java index be9d80012ccf0..88cf322ed9130 100644 --- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java @@ -16,44 +16,44 @@ package com.android.server.job.controllers; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; +import android.net.INetworkPolicyListener; import android.net.NetworkInfo; -import android.os.ServiceManager; +import android.net.NetworkPolicyManager; import android.os.UserHandle; import android.util.Slog; -import com.android.server.ConnectivityService; +import com.android.internal.annotations.GuardedBy; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateChangedListener; import java.io.PrintWriter; -import java.util.LinkedList; -import java.util.List; +import java.util.ArrayList; /** * Handles changes in connectivity. - * We are only interested in metered vs. unmetered networks, and we're interested in them on a - * per-user basis. + *

+ * Each app can have a different default networks or different connectivity + * status due to user-requested network policies, so we need to check + * constraints on a per-UID basis. */ public class ConnectivityController extends StateController implements ConnectivityManager.OnNetworkActiveListener { private static final String TAG = "JobScheduler.Conn"; - private final List mTrackedJobs = new LinkedList(); - private final BroadcastReceiver mConnectivityChangedReceiver = - new ConnectivityChangedReceiver(); + private final ConnectivityManager mConnManager; + private final NetworkPolicyManager mNetPolicyManager; + + @GuardedBy("mLock") + private final ArrayList mTrackedJobs = new ArrayList(); + /** Singleton. */ private static ConnectivityController mSingleton; private static Object sCreationLock = new Object(); - /** Track whether the latest active network is metered. */ - private boolean mNetworkUnmetered; - /** Track whether the latest active network is connected. */ - private boolean mNetworkConnected; public static ConnectivityController get(JobSchedulerService jms) { synchronized (sCreationLock) { @@ -67,51 +67,62 @@ public class ConnectivityController extends StateController implements private ConnectivityController(StateChangedListener stateChangedListener, Context context, Object lock) { super(stateChangedListener, context, lock); - // Register connectivity changed BR. - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + + mConnManager = mContext.getSystemService(ConnectivityManager.class); + mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class); + + final IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); mContext.registerReceiverAsUser( - mConnectivityChangedReceiver, UserHandle.ALL, intentFilter, null, null); - ConnectivityService cs = - (ConnectivityService)ServiceManager.getService(Context.CONNECTIVITY_SERVICE); - if (cs != null) { - if (cs.getActiveNetworkInfo() != null) { - mNetworkConnected = cs.getActiveNetworkInfo().isConnected(); - mNetworkUnmetered = mNetworkConnected && !cs.isActiveNetworkMetered(); - } else { - mNetworkConnected = mNetworkUnmetered = false; - } - } + mConnectivityReceiver, UserHandle.SYSTEM, intentFilter, null, null); + + mNetPolicyManager.registerListener(mNetPolicyListener); } @Override public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { - if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) { - jobStatus.setConnectivityConstraintSatisfied(mNetworkConnected); - jobStatus.setUnmeteredConstraintSatisfied(mNetworkUnmetered); + if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint() + || jobStatus.hasNotRoamingConstraint()) { + updateConstraintsSatisfied(jobStatus); mTrackedJobs.add(jobStatus); } } @Override - public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) { - if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) { + public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, + boolean forUpdate) { + if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint() + || jobStatus.hasNotRoamingConstraint()) { mTrackedJobs.remove(jobStatus); } } + private boolean updateConstraintsSatisfied(JobStatus jobStatus) { + final NetworkInfo info = mConnManager.getActiveNetworkInfoForUid(jobStatus.getSourceUid()); + final boolean connected = (info != null) && info.isConnected(); + final boolean unmetered = connected && !info.isMetered(); + final boolean notRoaming = connected && !info.isRoaming(); + + boolean changed = false; + changed |= jobStatus.setConnectivityConstraintSatisfied(connected); + changed |= jobStatus.setUnmeteredConstraintSatisfied(unmetered); + changed |= jobStatus.setNotRoamingConstraintSatisfied(notRoaming); + return changed; + } + /** - * @param userId Id of the user for whom we are updating the connectivity state. + * Update all jobs tracked by this controller. + * + * @param uid only update jobs belonging to this UID, or {@code -1} to + * update all tracked jobs. */ - private void updateTrackedJobs(int userId) { + private void updateTrackedJobs(int uid) { synchronized (mLock) { boolean changed = false; - for (JobStatus js : mTrackedJobs) { - if (js.getUserId() != userId) { - continue; + for (int i = 0; i < mTrackedJobs.size(); i++) { + final JobStatus js = mTrackedJobs.get(i); + if (uid == -1 || uid == js.getSourceUid()) { + changed |= updateConstraintsSatisfied(js); } - changed |= js.setConnectivityConstraintSatisfied(mNetworkConnected); - changed |= js.setUnmeteredConstraintSatisfied(mNetworkUnmetered); } if (changed) { mStateChangedListener.onControllerStateChanged(); @@ -122,9 +133,11 @@ public class ConnectivityController extends StateController implements /** * We know the network has just come up. We want to run any jobs that are ready. */ + @Override public synchronized void onNetworkActive() { synchronized (mLock) { - for (JobStatus js : mTrackedJobs) { + for (int i = 0; i < mTrackedJobs.size(); i++) { + final JobStatus js = mTrackedJobs.get(i); if (js.isReady()) { if (DEBUG) { Slog.d(TAG, "Running " + js + " due to network activity."); @@ -135,61 +148,44 @@ public class ConnectivityController extends StateController implements } } - class ConnectivityChangedReceiver extends BroadcastReceiver { - /** - * We'll receive connectivity changes for each user here, which we process independently. - * We are only interested in the active network here. We're only interested in the active - * network, b/c the end result of this will be for apps to try to hit the network. - * @param context The Context in which the receiver is running. - * @param intent The Intent being received. - */ - // TODO: Test whether this will be called twice for each user. + private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (DEBUG) { - Slog.d(TAG, "Received connectivity event: " + intent.getAction() + " u" - + context.getUserId()); - } - final String action = intent.getAction(); - if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - final int networkType = - intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, - ConnectivityManager.TYPE_NONE); - // Connectivity manager for THIS context - important! - final ConnectivityManager connManager = (ConnectivityManager) - context.getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo(); - final int userid = context.getUserId(); - // This broadcast gets sent a lot, only update if the active network has changed. - if (activeNetwork == null) { - mNetworkUnmetered = false; - mNetworkConnected = false; - updateTrackedJobs(userid); - } else if (activeNetwork.getType() == networkType) { - mNetworkUnmetered = false; - mNetworkConnected = !intent.getBooleanExtra( - ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); - if (mNetworkConnected) { // No point making the call if we know there's no conn. - mNetworkUnmetered = !connManager.isActiveNetworkMetered(); - } - updateTrackedJobs(userid); - } - } else { - if (DEBUG) { - Slog.d(TAG, "Unrecognised action in intent: " + action); - } - } + updateTrackedJobs(-1); + } + }; + + private INetworkPolicyListener mNetPolicyListener = new INetworkPolicyListener.Stub() { + @Override + public void onUidRulesChanged(int uid, int uidRules) { + updateTrackedJobs(uid); + } + + @Override + public void onMeteredIfacesChanged(String[] meteredIfaces) { + updateTrackedJobs(-1); + } + + @Override + public void onRestrictBackgroundChanged(boolean restrictBackground) { + updateTrackedJobs(-1); + } + + @Override + public void onRestrictBackgroundWhitelistChanged(int uid, boolean whitelisted) { + updateTrackedJobs(uid); } }; @Override public void dumpControllerStateLocked(PrintWriter pw) { pw.println("Conn."); - pw.println("connected: " + mNetworkConnected + " unmetered: " + mNetworkUnmetered); - for (JobStatus js: mTrackedJobs) { + for (int i = 0; i < mTrackedJobs.size(); i++) { + final JobStatus js = mTrackedJobs.get(i); pw.println(String.valueOf(js.getJobId() + "," + js.getUid()) + ": C=" + js.hasConnectivityConstraint() - + ", UM=" + js.hasUnmeteredConstraint()); + + ", UM=" + js.hasUnmeteredConstraint() + + ", NR=" + js.hasNotRoamingConstraint()); } } } diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index dd70758007602..9ab4386e35a64 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -29,7 +29,6 @@ import android.util.ArraySet; import android.util.TimeUtils; import java.io.PrintWriter; -import java.util.concurrent.atomic.AtomicBoolean; /** * Uniquely identifies a job internally. @@ -55,6 +54,7 @@ public final class JobStatus { static final int CONSTRAINT_APP_NOT_IDLE = 1<<6; static final int CONSTRAINT_CONTENT_TRIGGER = 1<<7; static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<8; + static final int CONSTRAINT_NOT_ROAMING = 1<<9; // Soft override: ignore constraints like time that don't affect API availability public static final int OVERRIDE_SOFT = 1; @@ -176,6 +176,9 @@ public final class JobStatus { if (job.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED) { requiredConstraints |= CONSTRAINT_UNMETERED; } + if (job.getNetworkType() == JobInfo.NETWORK_TYPE_NOT_ROAMING) { + requiredConstraints |= CONSTRAINT_NOT_ROAMING; + } if (job.isRequireCharging()) { requiredConstraints |= CONSTRAINT_CHARGING; } @@ -312,6 +315,10 @@ public final class JobStatus { return (requiredConstraints&CONSTRAINT_UNMETERED) != 0; } + public boolean hasNotRoamingConstraint() { + return (requiredConstraints&CONSTRAINT_NOT_ROAMING) != 0; + } + public boolean hasChargingConstraint() { return (requiredConstraints&CONSTRAINT_CHARGING) != 0; } @@ -376,12 +383,16 @@ public final class JobStatus { return setConstraintSatisfied(CONSTRAINT_IDLE, state); } + boolean setConnectivityConstraintSatisfied(boolean state) { + return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state); + } + boolean setUnmeteredConstraintSatisfied(boolean state) { return setConstraintSatisfied(CONSTRAINT_UNMETERED, state); } - boolean setConnectivityConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state); + boolean setNotRoamingConstraintSatisfied(boolean state) { + return setConstraintSatisfied(CONSTRAINT_NOT_ROAMING, state); } boolean setAppNotIdleConstraintSatisfied(boolean state) { @@ -425,7 +436,7 @@ public final class JobStatus { static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_TIMING_DELAY | - CONSTRAINT_CONNECTIVITY | CONSTRAINT_UNMETERED | + CONSTRAINT_CONNECTIVITY | CONSTRAINT_UNMETERED | CONSTRAINT_NOT_ROAMING | CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER; // Soft override covers all non-"functional" constraints @@ -517,11 +528,14 @@ public final class JobStatus { if ((constraints&CONSTRAINT_IDLE) != 0) { pw.print(" IDLE"); } + if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) { + pw.print(" CONNECTIVITY"); + } if ((constraints&CONSTRAINT_UNMETERED) != 0) { pw.print(" UNMETERED"); } - if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) { - pw.print(" CONNECTIVITY"); + if ((constraints&CONSTRAINT_NOT_ROAMING) != 0) { + pw.print(" NOT_ROAMING"); } if ((constraints&CONSTRAINT_APP_NOT_IDLE) != 0) { pw.print(" APP_NOT_IDLE"); diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index fc412e3dc9fa5..4a8539aa32827 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -209,7 +209,9 @@ public class LockdownVpnTracker { throw new RuntimeException("Problem setting firewall rules", e); } - mConnService.sendConnectedBroadcast(augmentNetworkInfo(egressInfo)); + final NetworkInfo clone = new NetworkInfo(egressInfo); + augmentNetworkInfo(clone); + mConnService.sendConnectedBroadcast(clone); } } @@ -320,13 +322,11 @@ public class LockdownVpnTracker { } } - public NetworkInfo augmentNetworkInfo(NetworkInfo info) { + public void augmentNetworkInfo(NetworkInfo info) { if (info.isConnected()) { final NetworkInfo vpnInfo = mVpn.getNetworkInfo(); - info = new NetworkInfo(info); info.setDetailedState(vpnInfo.getDetailedState(), vpnInfo.getReason(), null); } - return info; } private void showNotification(int titleRes, int iconRes) { diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 433c056104590..43f47fa3a2e1d 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -2148,6 +2148,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public boolean isNetworkMetered(NetworkState state) { + if (state.networkInfo == null) { + return false; + } + final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state); // roaming networks are always considered metered @@ -2163,10 +2167,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (policy != null) { return policy.metered; } else { - if (state.networkInfo == null) { - return false; - } - final int type = state.networkInfo.getType(); if (isNetworkTypeMobile(type) || type == TYPE_WIMAX) { return true;