Adding a callback to notify data usage.
Allow holders of android.Manifest.permission#PACKAGE_USAGE_STATS
to be notified when data usage has exceeded a given threshold.
This allows an app to update its data usage metrics without
polling.
Bug: 25812785
Change-Id: I3a4904a97f3c7fbaf8071b460f9ee6ca9c1ba4ed
diff --git a/api/current.txt b/api/current.txt
index 14c7c0d..7580897 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6315,6 +6315,22 @@
field public static final android.os.Parcelable.Creator<android.app.usage.ConfigurationStats> CREATOR;
}
+ public class DataUsagePolicy {
+ field public final int networkType;
+ field public final java.lang.String[] subscriberIds;
+ field public final long thresholdInBytes;
+ field public final int[] uids;
+ }
+
+ public static class DataUsagePolicy.Builder {
+ ctor public DataUsagePolicy.Builder();
+ method public android.app.usage.DataUsagePolicy.Builder addSubscriberId(java.lang.String);
+ method public android.app.usage.DataUsagePolicy.Builder addUid(int);
+ method public android.app.usage.DataUsagePolicy build();
+ method public android.app.usage.DataUsagePolicy.Builder setNetworkType(int);
+ method public android.app.usage.DataUsagePolicy.Builder setThreshold(long);
+ }
+
public final class NetworkStats implements java.lang.AutoCloseable {
method public void close();
method public boolean getNextBucket(android.app.usage.NetworkStats.Bucket);
@@ -6355,6 +6371,14 @@
method public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
method public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
method public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, java.lang.String, long, long, boolean) throws android.os.RemoteException, java.lang.SecurityException;
+ method public void registerDataUsageCallback(android.app.usage.DataUsagePolicy, android.app.usage.NetworkStatsManager.DataUsageCallback);
+ method public void registerDataUsageCallback(android.app.usage.DataUsagePolicy, android.app.usage.NetworkStatsManager.DataUsageCallback, android.os.Handler);
+ method public void unregisterDataUsageCallback(android.app.usage.NetworkStatsManager.DataUsageCallback);
+ }
+
+ public static class NetworkStatsManager.DataUsageCallback {
+ ctor public NetworkStatsManager.DataUsageCallback();
+ method public void onLimitReached();
}
public final class UsageEvents implements android.os.Parcelable {
@@ -23439,6 +23463,12 @@
method public int getUid();
}
+ public class DataUsageRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.DataUsageRequest> CREATOR;
+ }
+
public class DhcpInfo implements android.os.Parcelable {
ctor public DhcpInfo();
method public int describeContents();
diff --git a/api/system-current.txt b/api/system-current.txt
index f49a872..692a86d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6573,6 +6573,22 @@
field public static final android.os.Parcelable.Creator<android.app.usage.ConfigurationStats> CREATOR;
}
+ public class DataUsagePolicy {
+ field public final int networkType;
+ field public final java.lang.String[] subscriberIds;
+ field public final long thresholdInBytes;
+ field public final int[] uids;
+ }
+
+ public static class DataUsagePolicy.Builder {
+ ctor public DataUsagePolicy.Builder();
+ method public android.app.usage.DataUsagePolicy.Builder addSubscriberId(java.lang.String);
+ method public android.app.usage.DataUsagePolicy.Builder addUid(int);
+ method public android.app.usage.DataUsagePolicy build();
+ method public android.app.usage.DataUsagePolicy.Builder setNetworkType(int);
+ method public android.app.usage.DataUsagePolicy.Builder setThreshold(long);
+ }
+
public final class NetworkStats implements java.lang.AutoCloseable {
method public void close();
method public boolean getNextBucket(android.app.usage.NetworkStats.Bucket);
@@ -6613,6 +6629,14 @@
method public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
method public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
method public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, java.lang.String, long, long, boolean) throws android.os.RemoteException, java.lang.SecurityException;
+ method public void registerDataUsageCallback(android.app.usage.DataUsagePolicy, android.app.usage.NetworkStatsManager.DataUsageCallback);
+ method public void registerDataUsageCallback(android.app.usage.DataUsagePolicy, android.app.usage.NetworkStatsManager.DataUsageCallback, android.os.Handler);
+ method public void unregisterDataUsageCallback(android.app.usage.NetworkStatsManager.DataUsageCallback);
+ }
+
+ public static class NetworkStatsManager.DataUsageCallback {
+ ctor public NetworkStatsManager.DataUsageCallback();
+ method public void onLimitReached();
}
public final class UsageEvents implements android.os.Parcelable {
@@ -25016,6 +25040,12 @@
method public int getUid();
}
+ public class DataUsageRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.DataUsageRequest> CREATOR;
+ }
+
public class DhcpInfo implements android.os.Parcelable {
ctor public DhcpInfo();
method public int describeContents();
diff --git a/api/test-current.txt b/api/test-current.txt
index c6f791d..56e4689 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6317,6 +6317,22 @@
field public static final android.os.Parcelable.Creator<android.app.usage.ConfigurationStats> CREATOR;
}
+ public class DataUsagePolicy {
+ field public final int networkType;
+ field public final java.lang.String[] subscriberIds;
+ field public final long thresholdInBytes;
+ field public final int[] uids;
+ }
+
+ public static class DataUsagePolicy.Builder {
+ ctor public DataUsagePolicy.Builder();
+ method public android.app.usage.DataUsagePolicy.Builder addSubscriberId(java.lang.String);
+ method public android.app.usage.DataUsagePolicy.Builder addUid(int);
+ method public android.app.usage.DataUsagePolicy build();
+ method public android.app.usage.DataUsagePolicy.Builder setNetworkType(int);
+ method public android.app.usage.DataUsagePolicy.Builder setThreshold(long);
+ }
+
public final class NetworkStats implements java.lang.AutoCloseable {
method public void close();
method public boolean getNextBucket(android.app.usage.NetworkStats.Bucket);
@@ -6357,6 +6373,14 @@
method public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
method public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
method public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, java.lang.String, long, long, boolean) throws android.os.RemoteException, java.lang.SecurityException;
+ method public void registerDataUsageCallback(android.app.usage.DataUsagePolicy, android.app.usage.NetworkStatsManager.DataUsageCallback);
+ method public void registerDataUsageCallback(android.app.usage.DataUsagePolicy, android.app.usage.NetworkStatsManager.DataUsageCallback, android.os.Handler);
+ method public void unregisterDataUsageCallback(android.app.usage.NetworkStatsManager.DataUsageCallback);
+ }
+
+ public static class NetworkStatsManager.DataUsageCallback {
+ ctor public NetworkStatsManager.DataUsageCallback();
+ method public void onLimitReached();
}
public final class UsageEvents implements android.os.Parcelable {
@@ -23448,6 +23472,12 @@
method public int getUid();
}
+ public class DataUsageRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.DataUsageRequest> CREATOR;
+ }
+
public class DhcpInfo implements android.os.Parcelable {
ctor public DhcpInfo();
method public int describeContents();
diff --git a/core/java/android/app/usage/DataUsagePolicy.java b/core/java/android/app/usage/DataUsagePolicy.java
new file mode 100644
index 0000000..5a5dcbc
--- /dev/null
+++ b/core/java/android/app/usage/DataUsagePolicy.java
@@ -0,0 +1,174 @@
+/**
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package android.app.usage;
+
+import android.net.ConnectivityManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.IntArray;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Defines a policy for data usage callbacks, made through {@link DataUsagePolicy.Builder} and used
+ * to be notified on data usage via {@link NetworkStatsManager#registerDataUsageCallback}.
+ */
+public class DataUsagePolicy {
+
+ /**
+ * Network type to be monitored, as defined in {@link ConnectivityManager}, e.g.
+ * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} etc.
+ */
+ public final int networkType;
+
+ /**
+ * Set of subscriber ids to be monitored for the given network type. May be empty if not
+ * applicable.
+ * <p>Should not be modified once created.
+ */
+ public final String[] subscriberIds;
+
+ /**
+ * Set of UIDs of which to monitor data usage.
+ *
+ * <p>If not {@code null}, the caller will be notified when any of the uids exceed
+ * the given threshold. If empty all uids for which the calling process has access
+ * to stats will be monitored.
+ * <p>Should not be modified once created.
+ */
+ public final int[] uids;
+
+ /**
+ * Threshold in bytes to be notified on.
+ */
+ public final long thresholdInBytes;
+
+ /**
+ * @hide
+ */
+ DataUsagePolicy(int networkType, String[] subscriberIds, int[] uids,
+ long thresholdInBytes) {
+ this.networkType = networkType;
+ this.subscriberIds = subscriberIds;
+ this.uids = uids;
+ this.thresholdInBytes = thresholdInBytes;
+ }
+
+ /**
+ * Builder used to create {@link DataUsagePolicy} objects.
+ */
+ public static class Builder {
+ private static final int INVALID_NETWORK_TYPE = -1;
+ private int mNetworkType = INVALID_NETWORK_TYPE;
+ private List<String> mSubscriberList = new ArrayList<>();
+ private IntArray mUids = new IntArray();
+ private long mThresholdInBytes;
+
+ /**
+ * Default constructor for Builder.
+ */
+ public Builder() {}
+
+ /**
+ * Build {@link DataUsagePolicy} given the current policies.
+ */
+ public DataUsagePolicy build() {
+ if (mNetworkType == INVALID_NETWORK_TYPE) {
+ throw new IllegalArgumentException(
+ "DataUsagePolicy requires a valid network type to be set");
+ }
+ return new DataUsagePolicy(mNetworkType,
+ mSubscriberList.toArray(new String[mSubscriberList.size()]),
+ mUids.toArray(), mThresholdInBytes);
+ }
+
+ /**
+ * Specifies that the given {@code subscriberId} should be monitored.
+ *
+ * @param subscriberId the subscriber id of the network interface.
+ */
+ public Builder addSubscriberId(String subscriberId) {
+ mSubscriberList.add(subscriberId);
+ return this;
+ }
+
+ /**
+ * Specifies that the given {@code uid} should be monitored.
+ */
+ public Builder addUid(int uid) {
+ mUids.add(uid);
+ return this;
+ }
+
+ /**
+ * Specifies that the callback should monitor the given network. It is mandatory
+ * to set one.
+ *
+ * @param networkType As defined in {@link ConnectivityManager}, e.g.
+ * {@link ConnectivityManager#TYPE_MOBILE},
+ * {@link ConnectivityManager#TYPE_WIFI}, etc.
+ */
+ public Builder setNetworkType(int networkType) {
+ mNetworkType = networkType;
+ return this;
+ }
+
+ /**
+ * Sets the threshold in bytes on which the listener should be called. The framework may
+ * impose a minimum threshold to avoid too many notifications to be triggered.
+ */
+ public Builder setThreshold(long thresholdInBytes) {
+ mThresholdInBytes = thresholdInBytes;
+ return this;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DataUsagePolicy [ networkType=" + networkType
+ + ", subscriberIds=" + Arrays.toString(subscriberIds)
+ + ", uids=" + Arrays.toString(uids)
+ + ", thresholdInBytes=" + thresholdInBytes + " ]";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof DataUsagePolicy == false) return false;
+ DataUsagePolicy that = (DataUsagePolicy) obj;
+ return that.networkType == this.networkType
+ && Arrays.deepEquals(that.subscriberIds, this.subscriberIds)
+ && Arrays.equals(that.uids, this.uids)
+ && that.thresholdInBytes == this.thresholdInBytes;
+ }
+
+ @Override
+ public int hashCode() {
+ // Start with a non-zero constant.
+ int result = 17;
+
+ // Include a hash for each field.
+ result = 31 * result + networkType;
+ result = 31 * result + Arrays.deepHashCode(subscriberIds);
+ result = 31 * result + Arrays.hashCode(uids);
+ result = 31 * result + (int) (thresholdInBytes ^ (thresholdInBytes >>> 32));
+
+ return result;
+ }
+}
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 4dc636b..13aeef0 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -16,12 +16,17 @@
package android.app.usage;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.Nullable;
import android.app.usage.NetworkStats.Bucket;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.DataUsageRequest;
import android.net.NetworkIdentity;
import android.net.NetworkTemplate;
import android.os.Build;
+import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
@@ -288,6 +293,62 @@
return result;
}
+ /**
+ * Registers to receive notifications about data usage on specified networks and uids.
+ * The callbacks will continue to be called as long as the process is live or
+ * {@link #unregisterDataUsageCallback} is called.
+ *
+ * @param policy {@link DataUsagePolicy} describing this request.
+ * @param callback The {@link DataUsageCallback} that the system will call when data usage
+ * has exceeded the specified threshold.
+ */
+ public void registerDataUsageCallback(DataUsagePolicy policy, DataUsageCallback callback) {
+ registerDataUsageCallback(policy, callback, null /* handler */);
+ }
+
+ /**
+ * Registers to receive notifications about data usage on specified networks and uids.
+ * The callbacks will continue to be called as long as the process is live or
+ * {@link #unregisterDataUsageCallback} is called.
+ *
+ * @param policy {@link DataUsagePolicy} describing this request.
+ * @param callback The {@link DataUsageCallback} that the system will call when data usage
+ * has exceeded the specified threshold.
+ * @param handler to dispatch callback events through, otherwise if {@code null} it uses
+ * the calling thread.
+ */
+ public void registerDataUsageCallback(DataUsagePolicy policy, DataUsageCallback callback,
+ @Nullable Handler handler) {
+ checkNotNull(policy, "DataUsagePolicy cannot be null");
+ checkNotNull(callback, "DataUsageCallback cannot be null");
+
+ // TODO: Implement stub.
+ }
+
+ /**
+ * Unregisters callbacks on data usage.
+ *
+ * @param callback The {@link DataUsageCallback} used when registering.
+ */
+ public void unregisterDataUsageCallback(DataUsageCallback callback) {
+ checkNotNull(callback, "DataUsageCallback cannot be null");
+
+ // TODO: Implement stub.
+ }
+
+ /**
+ * Base class for data usage callbacks. Should be extended by applications wanting
+ * notifications.
+ */
+ public static class DataUsageCallback {
+ /**
+ * Called when data usage has reached the given policy threshold.
+ */
+ public void onLimitReached() {}
+
+ private DataUsageRequest request;
+ }
+
private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
NetworkTemplate template = null;
switch (networkType) {
diff --git a/core/java/android/net/DataUsageRequest.aidl b/core/java/android/net/DataUsageRequest.aidl
new file mode 100644
index 0000000..d1937c7
--- /dev/null
+++ b/core/java/android/net/DataUsageRequest.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+parcelable DataUsageRequest;
diff --git a/core/java/android/net/DataUsageRequest.java b/core/java/android/net/DataUsageRequest.java
new file mode 100644
index 0000000..0e46f4c
--- /dev/null
+++ b/core/java/android/net/DataUsageRequest.java
@@ -0,0 +1,143 @@
+/**
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package android.net;
+
+import android.net.NetworkTemplate;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Defines a request to register a callbacks. Used to be notified on data usage via
+ * {@link android.app.usage.NetworkStatsManager#registerDataUsageCallback}.
+ * If no {@code uid}s are set, callbacks are restricted to device-owners,
+ * carrier-privileged apps, or system apps.
+ */
+public class DataUsageRequest implements Parcelable {
+
+ /**
+ * @hide
+ */
+ public static final int REQUEST_ID_UNSET = 0;
+
+ /**
+ * Identifies the request. {@link DataUsageRequest}s should only be constructed by
+ * the Framework and it is used internally to identify the request.
+ * @hide
+ */
+ public final int requestId;
+
+ /**
+ * Set of {@link NetworkTemplate}s describing the networks to monitor.
+ * @hide
+ */
+ public final NetworkTemplate[] templates;
+
+ /**
+ * Set of UIDs of which to monitor data usage.
+ *
+ * <p>If not {@code null}, the caller will be notified when any of the uids exceed
+ * the given threshold. If {@code null} all uids for which the calling process has access
+ * to stats will be monitored.
+ * @hide
+ */
+ public final int[] uids;
+
+ /**
+ * Threshold in bytes to be notified on.
+ * @hide
+ */
+ public final long thresholdInBytes;
+
+ /**
+ * @hide
+ */
+ public DataUsageRequest(int requestId, NetworkTemplate[] templates, int[] uids,
+ long thresholdInBytes) {
+ this.requestId = requestId;
+ this.templates = templates;
+ this.uids = uids;
+ this.thresholdInBytes = thresholdInBytes;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(requestId);
+ dest.writeTypedArray(templates, flags);
+ dest.writeIntArray(uids);
+ dest.writeLong(thresholdInBytes);
+ }
+
+ public static final Creator<DataUsageRequest> CREATOR =
+ new Creator<DataUsageRequest>() {
+ @Override
+ public DataUsageRequest createFromParcel(Parcel in) {
+ int requestId = in.readInt();
+ NetworkTemplate[] templates = in.createTypedArray(NetworkTemplate.CREATOR);
+ int[] uids = in.createIntArray();
+ long thresholdInBytes = in.readLong();
+ DataUsageRequest result = new DataUsageRequest(requestId,
+ templates, uids, thresholdInBytes);
+ return result;
+ }
+
+ @Override
+ public DataUsageRequest[] newArray(int size) {
+ return new DataUsageRequest[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "DataUsageRequest [ requestId=" + requestId
+ + ", networkTemplates=" + Arrays.toString(templates)
+ + ", uids=" + Arrays.toString(uids)
+ + ", thresholdInBytes=" + thresholdInBytes + " ]";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof DataUsageRequest == false) return false;
+ DataUsageRequest that = (DataUsageRequest) obj;
+ return that.requestId == this.requestId
+ && Arrays.deepEquals(that.templates, this.templates)
+ && Arrays.equals(that.uids, this.uids)
+ && that.thresholdInBytes == this.thresholdInBytes;
+ }
+
+ @Override
+ public int hashCode() {
+ // Start with a non-zero constant.
+ int result = 17;
+
+ // Include a hash for each field.
+ result = 31 * result + requestId;
+ result = 31 * result + Arrays.deepHashCode(templates);
+ result = 31 * result + Arrays.hashCode(uids);
+ result = 31 * result + (int) (thresholdInBytes ^ (thresholdInBytes >>> 32));
+
+ return result;
+ }
+
+}