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 36a56b6e02c07..2755bc844f59c 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); @@ -6614,6 +6616,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 } @@ -6661,6 +6664,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 @@ -25584,8 +25588,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;