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;
+   }
+
+}