Merge "Fix bug 5090996 - Crash on tapping Menu on dial pad"
diff --git a/api/current.txt b/api/current.txt
index 251f62b..7d0503d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -90,10 +90,8 @@
     field public static final java.lang.String RECEIVE_SMS = "android.permission.RECEIVE_SMS";
     field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
     field public static final java.lang.String RECORD_AUDIO = "android.permission.RECORD_AUDIO";
-    field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS";
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
-    field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
     field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
     field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -117,7 +115,6 @@
     field public static final java.lang.String USE_CREDENTIALS = "android.permission.USE_CREDENTIALS";
     field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
     field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";
-    field public static final java.lang.String VPN = "android.permission.VPN";
     field public static final java.lang.String WAKE_LOCK = "android.permission.WAKE_LOCK";
     field public static final java.lang.String WRITE_APN_SETTINGS = "android.permission.WRITE_APN_SETTINGS";
     field public static final java.lang.String WRITE_CALENDAR = "android.permission.WRITE_CALENDAR";
@@ -11340,6 +11337,7 @@
     method public static long getUidUdpRxPackets(int);
     method public static long getUidUdpTxBytes(int);
     method public static long getUidUdpTxPackets(int);
+    method public static void incrementOperationCount(int, int);
     method public static void setThreadStatsTag(int);
     method public static deprecated void setThreadStatsTag(java.lang.String);
     method public static void tagSocket(java.net.Socket) throws java.net.SocketException;
@@ -11481,26 +11479,6 @@
     method public abstract java.lang.String sanitize(java.lang.String);
   }
 
-  public class VpnBuilder {
-    ctor public VpnBuilder();
-    method public android.net.VpnBuilder addAddress(java.lang.String, int);
-    method public android.net.VpnBuilder addAddress(java.net.InetAddress, int);
-    method public android.net.VpnBuilder addDnsServer(java.lang.String);
-    method public android.net.VpnBuilder addDnsServer(java.net.InetAddress);
-    method public android.net.VpnBuilder addRoute(java.lang.String, int);
-    method public android.net.VpnBuilder addRoute(java.net.InetAddress, int);
-    method public android.net.VpnBuilder addSearchDomain(java.lang.String);
-    method public android.os.ParcelFileDescriptor establish();
-    method public static android.content.Intent prepare(android.content.Context);
-    method public static boolean protect(int);
-    method public static boolean protect(java.net.Socket);
-    method public static boolean protect(java.net.DatagramSocket);
-    method public android.net.VpnBuilder setConfigureIntent(android.app.PendingIntent);
-    method public android.net.VpnBuilder setMtu(int);
-    method public android.net.VpnBuilder setSession(java.lang.String);
-    field public static final java.lang.String ACTION_VPN_REVOKED = "android.net.vpn.action.REVOKED";
-  }
-
 }
 
 package android.net.http {
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 0548250..c41d182 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -23,16 +23,21 @@
 /** {@hide} */
 interface INetworkStatsService {
 
-    /** Return historical stats for traffic that matches template. */
+    /** Return historical network layer stats for traffic that matches template. */
     NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template);
-    /** Return historical stats for specific UID traffic that matches template. */
+    /** Return historical network layer stats for specific UID traffic that matches template. */
     NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int tag);
 
-    /** Return usage summary for traffic that matches template. */
+    /** Return network layer usage summary for traffic that matches template. */
     NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end);
-    /** Return usage summary per UID for traffic that matches template. */
+    /** Return network layer usage summary per UID for traffic that matches template. */
     NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
 
+    /** Return data layer snapshot of UID network usage. */
+    NetworkStats getDataLayerSnapshotForUid(int uid);
+    /** Increment data layer count of operations performed for UID and tag. */
+    void incrementOperationCount(int uid, int tag, int operationCount);
+
     /** Force update of statistics. */
     void forceUpdate();
 
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index fbff7d8..0e8e7fc 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -21,6 +21,8 @@
 import android.os.SystemClock;
 import android.util.SparseBooleanArray;
 
+import com.android.internal.util.Objects;
+
 import java.io.CharArrayWriter;
 import java.io.PrintWriter;
 import java.util.Arrays;
@@ -56,6 +58,7 @@
     private long[] rxPackets;
     private long[] txBytes;
     private long[] txPackets;
+    private int[] operations;
 
     public static class Entry {
         public String iface;
@@ -65,12 +68,13 @@
         public long rxPackets;
         public long txBytes;
         public long txPackets;
+        public int operations;
 
         public Entry() {
         }
 
         public Entry(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes,
-                long txPackets) {
+                long txPackets, int operations) {
             this.iface = iface;
             this.uid = uid;
             this.tag = tag;
@@ -78,6 +82,7 @@
             this.rxPackets = rxPackets;
             this.txBytes = txBytes;
             this.txPackets = txPackets;
+            this.operations = operations;
         }
     }
 
@@ -91,6 +96,7 @@
         this.rxPackets = new long[initialSize];
         this.txBytes = new long[initialSize];
         this.txPackets = new long[initialSize];
+        this.operations = new int[initialSize];
     }
 
     public NetworkStats(Parcel parcel) {
@@ -103,11 +109,32 @@
         rxPackets = parcel.createLongArray();
         txBytes = parcel.createLongArray();
         txPackets = parcel.createLongArray();
+        operations = parcel.createIntArray();
+    }
+
+    /** {@inheritDoc} */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(elapsedRealtime);
+        dest.writeInt(size);
+        dest.writeStringArray(iface);
+        dest.writeIntArray(uid);
+        dest.writeIntArray(tag);
+        dest.writeLongArray(rxBytes);
+        dest.writeLongArray(rxPackets);
+        dest.writeLongArray(txBytes);
+        dest.writeLongArray(txPackets);
+        dest.writeIntArray(operations);
     }
 
     public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
             long txBytes, long txPackets) {
-        return addValues(new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets));
+        return addValues(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, 0);
+    }
+
+    public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
+            long txBytes, long txPackets, int operations) {
+        return addValues(
+                new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
     }
 
     /**
@@ -124,6 +151,7 @@
             rxPackets = Arrays.copyOf(rxPackets, newLength);
             txBytes = Arrays.copyOf(txBytes, newLength);
             txPackets = Arrays.copyOf(txPackets, newLength);
+            operations = Arrays.copyOf(operations, newLength);
         }
 
         iface[size] = entry.iface;
@@ -133,6 +161,7 @@
         rxPackets[size] = entry.rxPackets;
         txBytes[size] = entry.txBytes;
         txPackets[size] = entry.txPackets;
+        operations[size] = entry.operations;
         size++;
 
         return this;
@@ -150,6 +179,7 @@
         entry.rxPackets = rxPackets[i];
         entry.txBytes = txBytes[i];
         entry.txPackets = txPackets[i];
+        entry.operations = operations[i];
         return entry;
     }
 
@@ -167,8 +197,9 @@
     }
 
     public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
-            long txBytes, long txPackets) {
-        return combineValues(new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets));
+            long txBytes, long txPackets, int operations) {
+        return combineValues(
+                new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
     }
 
     /**
@@ -186,6 +217,7 @@
             rxPackets[i] += entry.rxPackets;
             txBytes[i] += entry.txBytes;
             txPackets[i] += entry.txPackets;
+            operations[i] += entry.operations;
         }
         return this;
     }
@@ -195,7 +227,7 @@
      */
     public int findIndex(String iface, int uid, int tag) {
         for (int i = 0; i < size; i++) {
-            if (equal(iface, this.iface[i]) && uid == this.uid[i] && tag == this.tag[i]) {
+            if (Objects.equal(iface, this.iface[i]) && uid == this.uid[i] && tag == this.tag[i]) {
                 return i;
             }
         }
@@ -203,6 +235,22 @@
     }
 
     /**
+     * Splice in {@link #operations} from the given {@link NetworkStats} based
+     * on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface},
+     * since operation counts are at data layer.
+     */
+    public void spliceOperationsFrom(NetworkStats stats) {
+        for (int i = 0; i < size; i++) {
+            final int j = stats.findIndex(IFACE_ALL, uid[i], tag[i]);
+            if (j == -1) {
+                operations[i] = 0;
+            } else {
+                operations[i] = stats.operations[j];
+            }
+        }
+    }
+
+    /**
      * Return list of unique interfaces known by this data structure.
      */
     public String[] getUniqueIfaces() {
@@ -289,15 +337,17 @@
                 entry.rxPackets = rxPackets[i];
                 entry.txBytes = txBytes[i];
                 entry.txPackets = txPackets[i];
+                entry.operations = operations[i];
             } else {
                 // existing row, subtract remote value
                 entry.rxBytes = rxBytes[i] - value.rxBytes[j];
                 entry.rxPackets = rxPackets[i] - value.rxPackets[j];
                 entry.txBytes = txBytes[i] - value.txBytes[j];
                 entry.txPackets = txPackets[i] - value.txPackets[j];
+                entry.operations = operations[i] - value.operations[j];
                 if (enforceMonotonic
                         && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
-                                || entry.txPackets < 0)) {
+                                || entry.txPackets < 0 || entry.operations < 0)) {
                     throw new IllegalArgumentException("found non-monotonic values");
                 }
                 if (clampNegative) {
@@ -305,6 +355,7 @@
                     entry.rxPackets = Math.max(0, entry.rxPackets);
                     entry.txBytes = Math.max(0, entry.txBytes);
                     entry.txPackets = Math.max(0, entry.txPackets);
+                    entry.operations = Math.max(0, entry.operations);
                 }
             }
 
@@ -314,10 +365,6 @@
         return result;
     }
 
-    private static boolean equal(Object a, Object b) {
-        return a == b || (a != null && a.equals(b));
-    }
-
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix);
         pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
@@ -325,11 +372,12 @@
             pw.print(prefix);
             pw.print("  iface="); pw.print(iface[i]);
             pw.print(" uid="); pw.print(uid[i]);
-            pw.print(" tag="); pw.print(tag[i]);
+            pw.print(" tag=0x"); pw.print(Integer.toHexString(tag[i]));
             pw.print(" rxBytes="); pw.print(rxBytes[i]);
             pw.print(" rxPackets="); pw.print(rxPackets[i]);
             pw.print(" txBytes="); pw.print(txBytes[i]);
-            pw.print(" txPackets="); pw.println(txPackets[i]);
+            pw.print(" txPackets="); pw.print(txPackets[i]);
+            pw.print(" operations="); pw.println(operations[i]);
         }
     }
 
@@ -345,19 +393,6 @@
         return 0;
     }
 
-    /** {@inheritDoc} */
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeLong(elapsedRealtime);
-        dest.writeInt(size);
-        dest.writeStringArray(iface);
-        dest.writeIntArray(uid);
-        dest.writeIntArray(tag);
-        dest.writeLongArray(rxBytes);
-        dest.writeLongArray(rxPackets);
-        dest.writeLongArray(txBytes);
-        dest.writeLongArray(txPackets);
-    }
-
     public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
         public NetworkStats createFromParcel(Parcel in) {
             return new NetworkStats(in);
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 8bd1738..4ffabb1 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -16,6 +16,16 @@
 
 package android.net;
 
+import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
+import static android.net.NetworkStatsHistory.DataStreamUtils.readLongArray;
+import static android.net.NetworkStatsHistory.DataStreamUtils.writeLongArray;
+import static android.net.NetworkStatsHistory.ParcelUtils.readIntArray;
+import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
+import static android.net.NetworkStatsHistory.ParcelUtils.writeIntArray;
+import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
+
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -43,19 +53,26 @@
     private static final int VERSION_INIT = 1;
 
     // TODO: teach about varint encoding to use less disk space
-    // TODO: extend to record rxPackets/txPackets
+    // TODO: teach about omitting entire fields to reduce parcel pressure
+    // TODO: persist/restore packet and operation counts
 
     private final long bucketDuration;
     private int bucketCount;
     private long[] bucketStart;
     private long[] rxBytes;
+    private long[] rxPackets;
     private long[] txBytes;
+    private long[] txPackets;
+    private int[] operations;
 
     public static class Entry {
         public long bucketStart;
         public long bucketDuration;
         public long rxBytes;
+        public long rxPackets;
         public long txBytes;
+        public long txPackets;
+        public int operations;
     }
 
     public NetworkStatsHistory(long bucketDuration) {
@@ -66,15 +83,21 @@
         this.bucketDuration = bucketDuration;
         bucketStart = new long[initialSize];
         rxBytes = new long[initialSize];
+        rxPackets = new long[initialSize];
         txBytes = new long[initialSize];
+        txPackets = new long[initialSize];
+        operations = new int[initialSize];
         bucketCount = 0;
     }
 
     public NetworkStatsHistory(Parcel in) {
         bucketDuration = in.readLong();
         bucketStart = readLongArray(in);
-        rxBytes = in.createLongArray();
-        txBytes = in.createLongArray();
+        rxBytes = readLongArray(in);
+        rxPackets = readLongArray(in);
+        txBytes = readLongArray(in);
+        txPackets = readLongArray(in);
+        operations = readIntArray(in);
         bucketCount = bucketStart.length;
     }
 
@@ -83,17 +106,24 @@
         out.writeLong(bucketDuration);
         writeLongArray(out, bucketStart, bucketCount);
         writeLongArray(out, rxBytes, bucketCount);
+        writeLongArray(out, rxPackets, bucketCount);
         writeLongArray(out, txBytes, bucketCount);
+        writeLongArray(out, txPackets, bucketCount);
+        writeIntArray(out, operations, bucketCount);
     }
 
     public NetworkStatsHistory(DataInputStream in) throws IOException {
+        // TODO: read packet and operation counts
         final int version = in.readInt();
         switch (version) {
             case VERSION_INIT: {
                 bucketDuration = in.readLong();
                 bucketStart = readLongArray(in);
                 rxBytes = readLongArray(in);
+                rxPackets = new long[bucketStart.length];
                 txBytes = readLongArray(in);
+                txPackets = new long[bucketStart.length];
+                operations = new int[bucketStart.length];
                 bucketCount = bucketStart.length;
                 break;
             }
@@ -104,6 +134,7 @@
     }
 
     public void writeToStream(DataOutputStream out) throws IOException {
+        // TODO: write packet and operation counts
         out.writeInt(VERSION_INIT);
         out.writeLong(bucketDuration);
         writeLongArray(out, bucketStart, bucketCount);
@@ -148,7 +179,10 @@
         entry.bucketStart = bucketStart[i];
         entry.bucketDuration = bucketDuration;
         entry.rxBytes = rxBytes[i];
+        entry.rxPackets = rxPackets[i];
         entry.txBytes = txBytes[i];
+        entry.txPackets = txPackets[i];
+        entry.operations = operations[i];
         return entry;
     }
 
@@ -156,17 +190,27 @@
      * Record that data traffic occurred in the given time range. Will
      * distribute across internal buckets, creating new buckets as needed.
      */
-    public void recordData(long start, long end, long rx, long tx) {
-        if (rx < 0 || tx < 0) {
-            throw new IllegalArgumentException(
-                    "tried recording negative data: rx=" + rx + ", tx=" + tx);
+    @Deprecated
+    public void recordData(long start, long end, long rxBytes, long txBytes) {
+        recordData(start, end,
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0));
+    }
+
+    /**
+     * Record that data traffic occurred in the given time range. Will
+     * distribute across internal buckets, creating new buckets as needed.
+     */
+    public void recordData(long start, long end, NetworkStats.Entry entry) {
+        if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 || entry.txPackets < 0
+                || entry.operations < 0) {
+            throw new IllegalArgumentException("tried recording negative data");
         }
 
         // create any buckets needed by this range
         ensureBuckets(start, end);
 
         // distribute data usage into buckets
-        final long duration = end - start;
+        long duration = end - start;
         for (int i = bucketCount - 1; i >= 0; i--) {
             final long curStart = bucketStart[i];
             final long curEnd = curStart + bucketDuration;
@@ -177,10 +221,22 @@
             if (curStart > end) continue;
 
             final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
-            if (overlap > 0) {
-                this.rxBytes[i] += rx * overlap / duration;
-                this.txBytes[i] += tx * overlap / duration;
-            }
+            if (overlap <= 0) continue;
+
+            // integer math each time is faster than floating point
+            final long fracRxBytes = entry.rxBytes * overlap / duration;
+            final long fracRxPackets = entry.rxPackets * overlap / duration;
+            final long fracTxBytes = entry.txBytes * overlap / duration;
+            final long fracTxPackets = entry.txPackets * overlap / duration;
+            final int fracOperations = (int) (entry.operations * overlap / duration);
+
+            rxBytes[i] += fracRxBytes; entry.rxBytes -= fracRxBytes;
+            rxPackets[i] += fracRxPackets; entry.rxPackets -= fracRxPackets;
+            txBytes[i] += fracTxBytes; entry.txBytes -= fracTxBytes;
+            txPackets[i] += fracTxPackets; entry.txPackets -= fracTxPackets;
+            operations[i] += fracOperations; entry.operations -= fracOperations;
+
+            duration -= overlap;
         }
     }
 
@@ -189,10 +245,19 @@
      * for combining together stats for external reporting.
      */
     public void recordEntireHistory(NetworkStatsHistory input) {
+        final NetworkStats.Entry entry = new NetworkStats.Entry(
+                IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0);
         for (int i = 0; i < input.bucketCount; i++) {
             final long start = input.bucketStart[i];
             final long end = start + input.bucketDuration;
-            recordData(start, end, input.rxBytes[i], input.txBytes[i]);
+
+            entry.rxBytes = input.rxBytes[i];
+            entry.rxPackets = input.rxPackets[i];
+            entry.txBytes = input.txBytes[i];
+            entry.txPackets = input.txPackets[i];
+            entry.operations = input.operations[i];
+
+            recordData(start, end, entry);
         }
     }
 
@@ -223,7 +288,10 @@
             final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
             bucketStart = Arrays.copyOf(bucketStart, newLength);
             rxBytes = Arrays.copyOf(rxBytes, newLength);
+            rxPackets = Arrays.copyOf(rxPackets, newLength);
             txBytes = Arrays.copyOf(txBytes, newLength);
+            txPackets = Arrays.copyOf(txPackets, newLength);
+            operations = Arrays.copyOf(operations, newLength);
         }
 
         // create gap when inserting bucket in middle
@@ -233,12 +301,18 @@
 
             System.arraycopy(bucketStart, index, bucketStart, dstPos, length);
             System.arraycopy(rxBytes, index, rxBytes, dstPos, length);
+            System.arraycopy(rxPackets, index, rxPackets, dstPos, length);
             System.arraycopy(txBytes, index, txBytes, dstPos, length);
+            System.arraycopy(txPackets, index, txPackets, dstPos, length);
+            System.arraycopy(operations, index, operations, dstPos, length);
         }
 
         bucketStart[index] = start;
         rxBytes[index] = 0;
+        rxPackets[index] = 0;
         txBytes[index] = 0;
+        txPackets[index] = 0;
+        operations[index] = 0;
         bucketCount++;
     }
 
@@ -260,7 +334,10 @@
             final int length = bucketStart.length;
             bucketStart = Arrays.copyOfRange(bucketStart, i, length);
             rxBytes = Arrays.copyOfRange(rxBytes, i, length);
+            rxPackets = Arrays.copyOfRange(rxPackets, i, length);
             txBytes = Arrays.copyOfRange(txBytes, i, length);
+            txPackets = Arrays.copyOfRange(txPackets, i, length);
+            operations = Arrays.copyOfRange(operations, i, length);
             bucketCount -= i;
         }
     }
@@ -282,7 +359,10 @@
         entry.bucketStart = start;
         entry.bucketDuration = end - start;
         entry.rxBytes = 0;
+        entry.rxPackets = 0;
         entry.txBytes = 0;
+        entry.txPackets = 0;
+        entry.operations = 0;
 
         for (int i = bucketCount - 1; i >= 0; i--) {
             final long curStart = bucketStart[i];
@@ -295,14 +375,16 @@
 
             // include full value for active buckets, otherwise only fractional
             final boolean activeBucket = curStart < now && curEnd > now;
-            final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
-            if (activeBucket || overlap == bucketDuration) {
-                entry.rxBytes += rxBytes[i];
-                entry.txBytes += txBytes[i];
-            } else if (overlap > 0) {
-                entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
-                entry.txBytes += txBytes[i] * overlap / bucketDuration;
-            }
+            final long overlap = activeBucket ? bucketDuration
+                    : Math.min(curEnd, end) - Math.max(curStart, start);
+            if (overlap <= 0) continue;
+
+            // integer math each time is faster than floating point
+            entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
+            entry.rxPackets += rxPackets[i] * overlap / bucketDuration;
+            entry.txBytes += txBytes[i] * overlap / bucketDuration;
+            entry.txPackets += txPackets[i] * overlap / bucketDuration;
+            entry.operations += operations[i] * overlap / bucketDuration;
         }
 
         return entry;
@@ -315,17 +397,19 @@
     public void generateRandom(long start, long end, long rx, long tx) {
         ensureBuckets(start, end);
 
+        final NetworkStats.Entry entry = new NetworkStats.Entry(
+                IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0);
         final Random r = new Random();
         while (rx > 1024 && tx > 1024) {
             final long curStart = randomLong(r, start, end);
             final long curEnd = randomLong(r, curStart, end);
-            final long curRx = randomLong(r, 0, rx);
-            final long curTx = randomLong(r, 0, tx);
+            entry.rxBytes = randomLong(r, 0, rx);
+            entry.txBytes = randomLong(r, 0, tx);
 
-            recordData(curStart, curEnd, curRx, curTx);
+            recordData(curStart, curEnd, entry);
 
-            rx -= curRx;
-            tx -= curTx;
+            rx -= entry.rxBytes;
+            tx -= entry.txBytes;
         }
     }
 
@@ -347,7 +431,10 @@
             pw.print(prefix);
             pw.print("  bucketStart="); pw.print(bucketStart[i]);
             pw.print(" rxBytes="); pw.print(rxBytes[i]);
-            pw.print(" txBytes="); pw.println(txBytes[i]);
+            pw.print(" rxPackets="); pw.print(rxPackets[i]);
+            pw.print(" txBytes="); pw.print(txBytes[i]);
+            pw.print(" txPackets="); pw.print(txPackets[i]);
+            pw.print(" operations="); pw.println(operations[i]);
         }
     }
 
@@ -368,41 +455,73 @@
         }
     };
 
-    private static long[] readLongArray(DataInputStream in) throws IOException {
-        final int size = in.readInt();
-        final long[] values = new long[size];
-        for (int i = 0; i < values.length; i++) {
-            values[i] = in.readLong();
+    /**
+     * Utility methods for interacting with {@link DataInputStream} and
+     * {@link DataOutputStream}, mostly dealing with writing partial arrays.
+     */
+    public static class DataStreamUtils {
+        public static long[] readLongArray(DataInputStream in) throws IOException {
+            final int size = in.readInt();
+            final long[] values = new long[size];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = in.readLong();
+            }
+            return values;
         }
-        return values;
-    }
 
-    private static void writeLongArray(DataOutputStream out, long[] values, int size) throws IOException {
-        if (size > values.length) {
-            throw new IllegalArgumentException("size larger than length");
-        }
-        out.writeInt(size);
-        for (int i = 0; i < size; i++) {
-            out.writeLong(values[i]);
+        public static void writeLongArray(DataOutputStream out, long[] values, int size)
+                throws IOException {
+            if (size > values.length) {
+                throw new IllegalArgumentException("size larger than length");
+            }
+            out.writeInt(size);
+            for (int i = 0; i < size; i++) {
+                out.writeLong(values[i]);
+            }
         }
     }
 
-    private static long[] readLongArray(Parcel in) {
-        final int size = in.readInt();
-        final long[] values = new long[size];
-        for (int i = 0; i < values.length; i++) {
-            values[i] = in.readLong();
+    /**
+     * Utility methods for interacting with {@link Parcel} structures, mostly
+     * dealing with writing partial arrays.
+     */
+    public static class ParcelUtils {
+        public static long[] readLongArray(Parcel in) {
+            final int size = in.readInt();
+            final long[] values = new long[size];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = in.readLong();
+            }
+            return values;
         }
-        return values;
-    }
 
-    private static void writeLongArray(Parcel out, long[] values, int size) {
-        if (size > values.length) {
-            throw new IllegalArgumentException("size larger than length");
+        public static void writeLongArray(Parcel out, long[] values, int size) {
+            if (size > values.length) {
+                throw new IllegalArgumentException("size larger than length");
+            }
+            out.writeInt(size);
+            for (int i = 0; i < size; i++) {
+                out.writeLong(values[i]);
+            }
         }
-        out.writeInt(size);
-        for (int i = 0; i < size; i++) {
-            out.writeLong(values[i]);
+
+        public static int[] readIntArray(Parcel in) {
+            final int size = in.readInt();
+            final int[] values = new int[size];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = in.readInt();
+            }
+            return values;
+        }
+
+        public static void writeIntArray(Parcel out, int[] values, int size) {
+            if (size > values.length) {
+                throw new IllegalArgumentException("size larger than length");
+            }
+            out.writeInt(size);
+            for (int i = 0; i < size; i++) {
+                out.writeInt(values[i]);
+            }
         }
     }
 
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index e054930..f138e49 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -20,14 +20,13 @@
 import android.app.backup.BackupManager;
 import android.content.Context;
 import android.media.MediaPlayer;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 
 import com.android.server.NetworkManagementSocketTagger;
 
 import dalvik.system.SocketTagger;
+
 import java.net.Socket;
 import java.net.SocketException;
 
@@ -172,7 +171,7 @@
             }
 
             // take snapshot in time; we calculate delta later
-            sActiveProfilingStart = getNetworkStatsForUid(context);
+            sActiveProfilingStart = getDataLayerSnapshotForUid(context);
         }
     }
 
@@ -190,7 +189,7 @@
             }
 
             // subtract starting values and return delta
-            final NetworkStats profilingStop = getNetworkStatsForUid(context);
+            final NetworkStats profilingStop = getDataLayerSnapshotForUid(context);
             final NetworkStats profilingDelta = profilingStop.subtractClamped(
                     sActiveProfilingStart);
             sActiveProfilingStart = null;
@@ -199,6 +198,28 @@
     }
 
     /**
+     * Increment count of network operations performed under the given
+     * accounting tag. This can be used to derive bytes-per-operation.
+     *
+     * @param tag Accounting tag used in {@link #setThreadStatsTag(int)}.
+     * @param operationCount Number of operations to increment count by.
+     */
+    public static void incrementOperationCount(int tag, int operationCount) {
+        if (operationCount < 0) {
+            throw new IllegalArgumentException("operation count can only be incremented");
+        }
+
+        final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+        final int uid = android.os.Process.myUid();
+        try {
+            statsService.incrementOperationCount(uid, tag, operationCount);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * Get the total number of packets transmitted through the mobile interface.
      *
      * @return number of packets.  If the statistics are not supported by this device,
@@ -461,14 +482,12 @@
      * Return detailed {@link NetworkStats} for the current UID. Requires no
      * special permission.
      */
-    private static NetworkStats getNetworkStatsForUid(Context context) {
-        final IBinder binder = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        final INetworkManagementService service = INetworkManagementService.Stub.asInterface(
-                binder);
-
+    private static NetworkStats getDataLayerSnapshotForUid(Context context) {
+        final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
         final int uid = android.os.Process.myUid();
         try {
-            return service.getNetworkStatsUidDetail(uid);
+            return statsService.getDataLayerSnapshotForUid(uid);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
diff --git a/core/java/android/net/VpnBuilder.java b/core/java/android/net/VpnBuilder.java
index 25cedb6..4582523 100644
--- a/core/java/android/net/VpnBuilder.java
+++ b/core/java/android/net/VpnBuilder.java
@@ -91,6 +91,7 @@
  *
  * <p class="note">Using this class requires
  * {@link android.Manifest.permission#VPN} permission.
+ * @hide
  */
 public class VpnBuilder {
 
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index cb96969..2c78679 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -274,8 +274,11 @@
             sStaticLayout = null;
         }
 
-        if (reflowed == null)
+        if (reflowed == null) {
             reflowed = new StaticLayout(true);
+        } else {
+            reflowed.prepare();
+        }
 
         reflowed.generate(text, where, where + after,
                 getPaint(), getWidth(), getAlignment(), getTextDirectionHeuristic(),
@@ -356,6 +359,7 @@
 
         synchronized (sLock) {
             sStaticLayout = reflowed;
+            reflowed.finish();
         }
     }
 
@@ -485,7 +489,7 @@
     private int mTopPadding, mBottomPadding;
 
     private static StaticLayout sStaticLayout = new StaticLayout(true);
-    private static Object sLock = new Object();
+    private static final Object[] sLock = new Object[0];
 
     private static final int START = 0;
     private static final int DIR = START;
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index 2920ac5..c184c11 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -29,14 +29,15 @@
  */
 class MeasuredText {
     private static final boolean localLOGV = false;
-    /* package */ CharSequence mText;
-    /* package */ int mTextStart;
-    /* package */ float[] mWidths;
-    /* package */ char[] mChars;
-    /* package */ byte[] mLevels;
-    /* package */ int mDir;
-    /* package */ boolean mEasy;
-    /* package */ int mLen;
+    CharSequence mText;
+    int mTextStart;
+    float[] mWidths;
+    char[] mChars;
+    byte[] mLevels;
+    int mDir;
+    boolean mEasy;
+    int mLen;
+
     private int mPos;
     private TextPaint mWorkPaint;
 
@@ -44,16 +45,16 @@
         mWorkPaint = new TextPaint();
     }
 
-    private static MeasuredText[] cached = new MeasuredText[3];
+    private static final Object[] sLock = new Object[0];
+    private static MeasuredText[] sCached = new MeasuredText[3];
 
-    /* package */
     static MeasuredText obtain() {
         MeasuredText mt;
-        synchronized (cached) {
-            for (int i = cached.length; --i >= 0;) {
-                if (cached[i] != null) {
-                    mt = cached[i];
-                    cached[i] = null;
+        synchronized (sLock) {
+            for (int i = sCached.length; --i >= 0;) {
+                if (sCached[i] != null) {
+                    mt = sCached[i];
+                    sCached[i] = null;
                     return mt;
                 }
             }
@@ -65,14 +66,14 @@
         return mt;
     }
 
-    /* package */
     static MeasuredText recycle(MeasuredText mt) {
         mt.mText = null;
         if (mt.mLen < 1000) {
-            synchronized(cached) {
-                for (int i = 0; i < cached.length; ++i) {
-                    if (cached[i] == null) {
-                        cached[i] = mt;
+            synchronized(sLock) {
+                for (int i = 0; i < sCached.length; ++i) {
+                    if (sCached[i] == null) {
+                        sCached[i] = mt;
+                        mt.mText = null;
                         break;
                     }
                 }
@@ -84,7 +85,6 @@
     /**
      * Analyzes text for bidirectional runs.  Allocates working buffers.
      */
-    /* package */
     void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
         mText = text;
         mTextStart = start;
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index f7b9502..14c71b2 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -923,6 +923,14 @@
     public void setMaximumVisibleLineCount(int lineCount) {
         mMaximumVisibleLineCount = lineCount;
     }
+    
+    void prepare() {
+        mMeasured = MeasuredText.obtain();
+    }
+    
+    void finish() {
+        mMeasured = MeasuredText.recycle(mMeasured);
+    }
 
     private int mLineCount;
     private int mTopPadding, mBottomPadding;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index dd7eaa9..914824c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5347,12 +5347,6 @@
                     || action == MotionEvent.ACTION_HOVER_MOVE
                     || action == MotionEvent.ACTION_HOVER_EXIT) {
                 if (dispatchHoverEvent(event)) {
-                    // For compatibility with existing applications that handled HOVER_MOVE
-                    // events in onGenericMotionEvent, dispatch the event there.  The
-                    // onHoverEvent method did not exist at the time.
-                    if (action == MotionEvent.ACTION_HOVER_MOVE) {
-                        dispatchGenericMotionEventInternal(event);
-                    }
                     return true;
                 }
             } else if (dispatchGenericPointerEvent(event)) {
@@ -5400,21 +5394,6 @@
      * @return True if the event was handled by the view, false otherwise.
      */
     protected boolean dispatchHoverEvent(MotionEvent event) {
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_HOVER_ENTER:
-                if (!hasHoveredChild() && !mSendingHoverAccessibilityEvents) {
-                    mSendingHoverAccessibilityEvents = true;
-                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
-                }
-                break;
-            case MotionEvent.ACTION_HOVER_EXIT:
-                if (mSendingHoverAccessibilityEvents) {
-                    mSendingHoverAccessibilityEvents = false;
-                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
-                }
-                break;
-        }
-
         //noinspection SimplifiableIfStatement
         if (mOnHoverListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                 && mOnHoverListener.onHover(this, event)) {
@@ -6055,7 +6034,8 @@
      * </p><p>
      * The default implementation calls {@link #setHovered} to update the hovered state
      * of the view when a hover enter or hover exit event is received, if the view
-     * is enabled and is clickable.
+     * is enabled and is clickable.  The default implementation also sends hover
+     * accessibility events.
      * </p>
      *
      * @param event The motion event that describes the hover.
@@ -6066,6 +6046,21 @@
      * @see #onHoverChanged
      */
     public boolean onHoverEvent(MotionEvent event) {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_HOVER_ENTER:
+                if (!hasHoveredChild() && !mSendingHoverAccessibilityEvents) {
+                    mSendingHoverAccessibilityEvents = true;
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+                }
+                break;
+            case MotionEvent.ACTION_HOVER_EXIT:
+                if (mSendingHoverAccessibilityEvents) {
+                    mSendingHoverAccessibilityEvents = false;
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+                }
+                break;
+        }
+
         if (isHoverable()) {
             switch (event.getAction()) {
                 case MotionEvent.ACTION_HOVER_ENTER:
@@ -6075,6 +6070,15 @@
                     setHovered(false);
                     break;
             }
+
+            // Dispatch the event to onGenericMotionEvent before returning true.
+            // This is to provide compatibility with existing applications that
+            // handled HOVER_MOVE events in onGenericMotionEvent and that would
+            // break because of the new default handling for hoverable views
+            // in onHoverEvent.
+            // Note that onGenericMotionEvent will be called by default when
+            // onHoverEvent returns false (refer to dispatchGenericMotionEvent).
+            dispatchGenericMotionEventInternal(event);
             return true;
         }
         return false;
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 1f29b16..946f009 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2428,7 +2428,7 @@
         if (mItemsCanFocus && (focusResult == null)
                 && selectedView != null && selectedView.hasFocus()) {
             final View focused = selectedView.findFocus();
-            if (distanceToView(focused) > 0) {
+            if (!isViewAncestorOf(focused, this) || distanceToView(focused) > 0) {
                 focused.clearFocus();
             }
         }
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java
index 59bef92..4667e5f 100644
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ b/core/java/com/android/server/NetworkManagementSocketTagger.java
@@ -69,8 +69,8 @@
     public void tag(FileDescriptor fd) throws SocketException {
         final SocketTags options = threadSocketTags.get();
         if (LOGD) {
-            Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=" + options.statsTag
-                    + ", statsUid=" + options.statsUid);
+            Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
+                    + Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
         }
         try {
             // TODO: skip tagging when options would be no-op
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 41056fd..819449a 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -846,14 +846,13 @@
     LOGV("%s", __FUNCTION__);
     native_data_t *nat = get_native_data(env, object);
     if (nat) {
-        DBusMessage *reply, *msg;
+        DBusMessage *msg;
         DBusMessageIter iter;
-        DBusError err;
+        dbus_bool_t reply = JNI_FALSE;
 
         const char *c_key = env->GetStringUTFChars(key, NULL);
         const char *c_path = env->GetStringUTFChars(path, NULL);
 
-        dbus_error_init(&err);
         msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
                                           c_path, DBUS_DEVICE_IFACE, "SetProperty");
         if (!msg) {
@@ -867,19 +866,14 @@
         dbus_message_iter_init_append(msg, &iter);
         append_variant(&iter, type, value);
 
-        reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
+        // Asynchronous call - the callbacks come via Device propertyChange
+        reply = dbus_connection_send_with_reply(nat->conn, msg, NULL, -1);
         dbus_message_unref(msg);
 
-        env->ReleaseStringUTFChars(key, c_key);
         env->ReleaseStringUTFChars(path, c_path);
-        if (!reply) {
-            if (dbus_error_is_set(&err)) {
-                LOG_AND_FREE_DBUS_ERROR(&err);
-            } else
-            LOGE("DBus reply is NULL in function %s", __FUNCTION__);
-            return JNI_FALSE;
-        }
-        return JNI_TRUE;
+        env->ReleaseStringUTFChars(key, c_key);
+
+        return reply ? JNI_TRUE : JNI_FALSE;
     }
 #endif
     return JNI_FALSE;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 92ba1d0..9d0901a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -393,7 +393,8 @@
         android:description="@string/permdesc_nfc"
         android:label="@string/permlab_nfc" />
 
-    <!-- Allows applications to provide VPN functionality -->
+    <!-- Allows applications to provide VPN functionality.
+         @hide Pending API council approval -->
     <permission android:name="android.permission.VPN"
         android:permissionGroup="android.permission-group.NETWORK"
         android:protectionLevel="dangerous"
@@ -663,7 +664,7 @@
         android:label="@string/permlab_reorderTasks"
         android:description="@string/permdesc_reorderTasks" />
 
-    <!-- Allows an application to change to remove/kill tasks -->
+    <!-- @hide Allows an application to change to remove/kill tasks -->
     <permission android:name="android.permission.REMOVE_TASKS"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
         android:protectionLevel="signature"
@@ -711,7 +712,7 @@
         android:label="@string/permlab_dump"
         android:description="@string/permdesc_dump" />
 
-    <!-- Allows an application to retrieve the content of the active window
+    <!-- @hide Allows an application to retrieve the content of the active window
          An active window is the window that has fired an accessibility event. -->
     <permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
         android:permissionGroup="android.permission-group.PERSONAL_INFO"
diff --git a/core/tests/coretests/res/raw/history_v1 b/core/tests/coretests/res/raw/history_v1
new file mode 100644
index 0000000..de79491
--- /dev/null
+++ b/core/tests/coretests/res/raw/history_v1
Binary files differ
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index 9403d95..242057c 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -23,16 +23,18 @@
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
 import static android.text.format.DateUtils.YEAR_IN_MILLIS;
 
+import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
-import junit.framework.TestCase;
+import com.android.frameworks.coretests.R;
 
+import java.io.DataInputStream;
 import java.util.Random;
 
 @SmallTest
-public class NetworkStatsHistoryTest extends TestCase {
+public class NetworkStatsHistoryTest extends AndroidTestCase {
     private static final String TAG = "NetworkStatsHistoryTest";
 
     private static final long TEST_START = 1194220800000L;
@@ -47,6 +49,32 @@
         }
     }
 
+    public void testReadOriginalVersion() throws Exception {
+        final DataInputStream in = new DataInputStream(
+                getContext().getResources().openRawResource(R.raw.history_v1));
+
+        NetworkStatsHistory.Entry entry = null;
+        try {
+            final NetworkStatsHistory history = new NetworkStatsHistory(in);
+            assertEquals(15 * SECOND_IN_MILLIS, history.getBucketDuration());
+
+            entry = history.getValues(0, entry);
+            assertEquals(29143L, entry.rxBytes);
+            assertEquals(6223L, entry.txBytes);
+
+            entry = history.getValues(history.size() - 1, entry);
+            assertEquals(1476L, entry.rxBytes);
+            assertEquals(838L, entry.txBytes);
+
+            entry = history.getValues(Long.MIN_VALUE, Long.MAX_VALUE, entry);
+            assertEquals(332401L, entry.rxBytes);
+            assertEquals(64314L, entry.txBytes);
+
+        } finally {
+            in.close();
+        }
+    }
+
     public void testRecordSingleBucket() throws Exception {
         final long BUCKET_SIZE = HOUR_IN_MILLIS;
         stats = new NetworkStatsHistory(BUCKET_SIZE);
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 2434e9f..69ad0f4 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -31,9 +31,9 @@
 
     public void testFindIndex() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L)
-                .addValues(TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L, 10)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L, 11)
+                .addValues(TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L, 12);
 
         assertEquals(2, stats.findIndex(TEST_IFACE, 102, TAG_NONE));
         assertEquals(2, stats.findIndex(TEST_IFACE, 102, TAG_NONE));
@@ -47,95 +47,95 @@
         assertEquals(0, stats.size());
         assertEquals(2, stats.internalSize());
 
-        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 1L, 2L, 2L);
-        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L, 2L, 2L);
+        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 1L, 2L, 2L, 3);
+        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L, 2L, 2L, 4);
 
         assertEquals(2, stats.size());
         assertEquals(2, stats.internalSize());
 
-        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 30L, 4L, 40L);
-        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 40L, 4L, 40L);
-        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 50L, 5L, 50L);
+        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 30L, 4L, 40L, 7);
+        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 40L, 4L, 40L, 8);
+        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 50L, 5L, 50L, 10);
 
         assertEquals(5, stats.size());
         assertTrue(stats.internalSize() >= 5);
 
-        assertEntry(stats, 0, TEST_IFACE, TEST_UID, TAG_NONE, 1L, 1L, 2L, 2L);
-        assertEntry(stats, 1, TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L, 2L, 2L);
-        assertEntry(stats, 2, TEST_IFACE, TEST_UID, TAG_NONE, 3L, 30L, 4L, 40L);
-        assertEntry(stats, 3, TEST_IFACE, TEST_UID, TAG_NONE, 4L, 40L, 4L, 40L);
-        assertEntry(stats, 4, TEST_IFACE, TEST_UID, TAG_NONE, 5L, 50L, 5L, 50L);
+        assertValues(stats, 0, TEST_IFACE, TEST_UID, TAG_NONE, 1L, 1L, 2L, 2L, 3);
+        assertValues(stats, 1, TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L, 2L, 2L, 4);
+        assertValues(stats, 2, TEST_IFACE, TEST_UID, TAG_NONE, 3L, 30L, 4L, 40L, 7);
+        assertValues(stats, 3, TEST_IFACE, TEST_UID, TAG_NONE, 4L, 40L, 4L, 40L, 8);
+        assertValues(stats, 4, TEST_IFACE, TEST_UID, TAG_NONE, 5L, 50L, 5L, 50L, 10);
     }
 
     public void testCombineExisting() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 10);
 
-        stats.addValues(TEST_IFACE, 1001, TAG_NONE, 512L, 4L, 256L, 2L);
-        stats.addValues(TEST_IFACE, 1001, 0xff, 128L, 1L, 128L, 1L);
-        stats.combineValues(TEST_IFACE, 1001, TAG_NONE, -128L, -1L, -128L, -1L);
+        stats.addValues(TEST_IFACE, 1001, TAG_NONE, 512L, 4L, 256L, 2L, 10);
+        stats.addValues(TEST_IFACE, 1001, 0xff, 128L, 1L, 128L, 1L, 2);
+        stats.combineValues(TEST_IFACE, 1001, TAG_NONE, -128L, -1L, -128L, -1L, -1);
 
-        assertEntry(stats, 0, TEST_IFACE, 1001, TAG_NONE, 384L, 3L, 128L, 1L);
-        assertEntry(stats, 1, TEST_IFACE, 1001, 0xff, 128L, 1L, 128L, 1L);
+        assertValues(stats, 0, TEST_IFACE, 1001, TAG_NONE, 384L, 3L, 128L, 1L, 9);
+        assertValues(stats, 1, TEST_IFACE, 1001, 0xff, 128L, 1L, 128L, 1L, 2);
 
         // now try combining that should create row
-        stats.combineValues(TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L);
-        assertEntry(stats, 2, TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L);
-        stats.combineValues(TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L);
-        assertEntry(stats, 2, TEST_IFACE, 5005, TAG_NONE, 256L, 2L, 256L, 2L);
+        stats.combineValues(TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L, 3);
+        assertValues(stats, 2, TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L, 3);
+        stats.combineValues(TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L, 3);
+        assertValues(stats, 2, TEST_IFACE, 5005, TAG_NONE, 256L, 2L, 256L, 2L, 6);
     }
 
     public void testSubtractIdenticalData() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats result = after.subtract(before);
 
         // identical data should result in zero delta
-        assertEntry(result, 0, TEST_IFACE, 100, TAG_NONE, 0L, 0L, 0L, 0L);
-        assertEntry(result, 1, TEST_IFACE, 101, TAG_NONE, 0L, 0L, 0L, 0L);
+        assertValues(result, 0, TEST_IFACE, 100, TAG_NONE, 0L, 0L, 0L, 0L, 0);
+        assertValues(result, 1, TEST_IFACE, 101, TAG_NONE, 0L, 0L, 0L, 0L, 0);
     }
 
     public void testSubtractIdenticalRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1025L, 9L, 2L, 1L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 3L, 1L, 1028L, 9L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
 
         final NetworkStats result = after.subtract(before);
 
         // expect delta between measurements
-        assertEntry(result, 0, TEST_IFACE, 100, TAG_NONE, 1L, 1L, 2L, 1L);
-        assertEntry(result, 1, TEST_IFACE, 101, TAG_NONE, 3L, 1L, 4L, 1L);
+        assertValues(result, 0, TEST_IFACE, 100, TAG_NONE, 1L, 1L, 2L, 1L, 4);
+        assertValues(result, 1, TEST_IFACE, 101, TAG_NONE, 3L, 1L, 4L, 1L, 8);
     }
 
     public void testSubtractNewRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L)
-                .addValues(TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
+                .addValues(TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
 
         final NetworkStats result = after.subtract(before);
 
         // its okay to have new rows
-        assertEntry(result, 0, TEST_IFACE, 100, TAG_NONE, 0L, 0L, 0L, 0L);
-        assertEntry(result, 1, TEST_IFACE, 101, TAG_NONE, 0L, 0L, 0L, 0L);
-        assertEntry(result, 2, TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L);
+        assertValues(result, 0, TEST_IFACE, 100, TAG_NONE, 0L, 0L, 0L, 0L, 0);
+        assertValues(result, 1, TEST_IFACE, 101, TAG_NONE, 0L, 0L, 0L, 0L, 0);
+        assertValues(result, 2, TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
     }
 
-    private static void assertEntry(NetworkStats stats, int index, String iface, int uid, int tag,
-            long rxBytes, long rxPackets, long txBytes, long txPackets) {
+    private static void assertValues(NetworkStats stats, int index, String iface, int uid, int tag,
+            long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) {
         final NetworkStats.Entry entry = stats.getValues(index, null);
         assertEquals(iface, entry.iface);
         assertEquals(uid, entry.uid);
@@ -144,6 +144,7 @@
         assertEquals(rxPackets, entry.rxPackets);
         assertEquals(txBytes, entry.txBytes);
         assertEquals(txPackets, entry.txPackets);
+        assertEquals(operations, entry.operations);
     }
 
 }
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 16755ad..4f51f03 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -168,18 +168,18 @@
         }
     }
 
+    const int minBufferSlots = mSynchronousMode ?
+            MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
     if (bufferCount == 0) {
-        const int minBufferSlots = mSynchronousMode ?
-                MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
         mClientBufferCount = 0;
         bufferCount = (mServerBufferCount >= minBufferSlots) ?
                 mServerBufferCount : minBufferSlots;
         return setBufferCountServerLocked(bufferCount);
     }
 
-    // We don't allow the client to set a buffer-count less than
-    // MIN_ASYNC_BUFFER_SLOTS (3), there is no reason for it.
-    if (bufferCount < MIN_ASYNC_BUFFER_SLOTS) {
+    if (bufferCount < minBufferSlots) {
+        LOGE("setBufferCount: requested buffer count (%d) is less than "
+                "minimum (%d)", bufferCount, minBufferSlots);
         return BAD_VALUE;
     }
 
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index e6837ea..d1037de 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -258,10 +258,10 @@
     int res = NO_ERROR;
     switch (operation) {
     case NATIVE_WINDOW_CONNECT:
-        res = dispatchConnect(args);
+        // deprecated. must return NO_ERROR.
         break;
     case NATIVE_WINDOW_DISCONNECT:
-        res = dispatchDisconnect(args);
+        // deprecated. must return NO_ERROR.
         break;
     case NATIVE_WINDOW_SET_USAGE:
         res = dispatchSetUsage(args);
@@ -296,6 +296,12 @@
     case NATIVE_WINDOW_SET_SCALING_MODE:
         res = dispatchSetScalingMode(args);
         break;
+    case NATIVE_WINDOW_API_CONNECT:
+        res = dispatchConnect(args);
+        break;
+    case NATIVE_WINDOW_API_DISCONNECT:
+        res = dispatchDisconnect(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 5db73db..7cbb39d 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -20,6 +20,9 @@
 // Turn on to check for OpenGL errors on each frame
 #define DEBUG_OPENGL 1
 
+// Turn on to display informations about the GPU
+#define DEBUG_EXTENSIONS 0
+
 // Turn on to enable initialization information
 #define DEBUG_INIT 0
 
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index eceb5c1c..38d1130 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -23,6 +23,8 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
+#include "Debug.h"
+
 namespace android {
 namespace uirenderer {
 
@@ -31,15 +33,20 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 // Debug
-#define DEBUG_EXTENSIONS 0
-
-// Debug
 #if DEBUG_EXTENSIONS
     #define EXT_LOGD(...) LOGD(__VA_ARGS__)
 #else
     #define EXT_LOGD(...)
 #endif
 
+// Vendor strings
+
+#define VENDOR_IMG "Imagination Technologies"
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes
+///////////////////////////////////////////////////////////////////////////////
+
 class Extensions {
 public:
     Extensions() {
@@ -58,17 +65,21 @@
         } while (head);
 
         mHasNPot = hasExtension("GL_OES_texture_npot");
-        mHasDrawPath = hasExtension("GL_NV_draw_path");
-        mHasCoverageSample = hasExtension("GL_NV_coverage_sample");
         mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");
 
+        const char* vendor = (const char*) glGetString(GL_VENDOR);
+        EXT_LOGD("Vendor: %s", vendor);
+        mNeedsHighpTexCoords = strcmp(vendor, VENDOR_IMG) == 0;
+
+        // We don't need to copy the string, the OpenGL ES spec
+        // guarantees the result of glGetString to point to a
+        // static string as long as our OpenGL context is valid
         mExtensions = buffer;
     }
 
     inline bool hasNPot() const { return mHasNPot; }
-    inline bool hasDrawPath() const { return mHasDrawPath; }
-    inline bool hasCoverageSample() const { return mHasCoverageSample; }
     inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
+    inline bool needsHighpTexCoords() const { return mNeedsHighpTexCoords; }
 
     bool hasExtension(const char* extension) const {
         const String8 s(extension);
@@ -85,8 +96,7 @@
     const char* mExtensions;
 
     bool mHasNPot;
-    bool mHasDrawPath;
-    bool mHasCoverageSample;
+    bool mNeedsHighpTexCoords;
     bool mHasFramebufferFetch;
 }; // class Extensions
 
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index d419e3e..c2383f4 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -18,6 +18,7 @@
 
 #include <utils/String8.h>
 
+#include "Caches.h"
 #include "ProgramCache.h"
 
 namespace android {
@@ -64,10 +65,18 @@
 const char* gVS_Header_Varyings_IsAA =
         "varying float widthProportion;\n"
         "varying float lengthProportion;\n";
-const char* gVS_Header_Varyings_HasBitmap =
-        "varying vec2 outBitmapTexCoords;\n";
-const char* gVS_Header_Varyings_PointHasBitmap =
-        "varying vec2 outPointBitmapTexCoords;\n";
+const char* gVS_Header_Varyings_HasBitmap[2] = {
+        // Default precision
+        "varying vec2 outBitmapTexCoords;\n",
+        // High precision
+        "varying highp vec2 outBitmapTexCoords;\n"
+};
+const char* gVS_Header_Varyings_PointHasBitmap[2] = {
+        // Default precision
+        "varying vec2 outPointBitmapTexCoords;\n",
+        // High precision
+        "varying highp vec2 outPointBitmapTexCoords;\n"
+};
 const char* gVS_Header_Varyings_HasGradient[3] = {
         // Linear
         "varying vec2 linear;\n",
@@ -417,9 +426,10 @@
         shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
     }
     if (description.hasBitmap) {
+        int index = Caches::getInstance().extensions.needsHighpTexCoords() ? 1 : 0;
         shader.append(description.isPoint ?
-                gVS_Header_Varyings_PointHasBitmap :
-                gVS_Header_Varyings_HasBitmap);
+                gVS_Header_Varyings_PointHasBitmap[index] :
+                gVS_Header_Varyings_HasBitmap[index]);
     }
 
     // Begin the shader
@@ -455,7 +465,6 @@
 }
 
 String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
-    // Set the default precision
     String8 shader;
 
     const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode;
@@ -479,9 +488,10 @@
         shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
     }
     if (description.hasBitmap) {
+        int index = Caches::getInstance().extensions.needsHighpTexCoords() ? 1 : 0;
         shader.append(description.isPoint ?
-                gVS_Header_Varyings_PointHasBitmap :
-                gVS_Header_Varyings_HasBitmap);
+                gVS_Header_Varyings_PointHasBitmap[index] :
+                gVS_Header_Varyings_HasBitmap[index]);
     }
 
     // Uniforms
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index 0e8ae61..8949730 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -317,6 +317,8 @@
         case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
         case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
         case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
+        case NATIVE_WINDOW_API_CONNECT:
+        case NATIVE_WINDOW_API_DISCONNECT:
             // TODO: we should implement these
             return NO_ERROR;
 
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 33ef1fc..e75415b 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -61,13 +61,19 @@
     const size_t c = list.size();
     for (size_t i=0 ; i<c ; i++) {
         const alloc_rec_t& rec(list.valueAt(i));
-        snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x\n",
-            list.keyAt(i), rec.size/1024.0f, 
-            rec.w, rec.s, rec.h, rec.format, rec.usage);
+        if (rec.size) {
+            snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x\n",
+                    list.keyAt(i), rec.size/1024.0f,
+                    rec.w, rec.s, rec.h, rec.format, rec.usage);
+        } else {
+            snprintf(buffer, SIZE, "%10p: unknown     | %4u (%4u) x %4u | %8X | 0x%08x\n",
+                    list.keyAt(i),
+                    rec.w, rec.s, rec.h, rec.format, rec.usage);
+        }
         result.append(buffer);
         total += rec.size;
     }
-    snprintf(buffer, SIZE, "Total allocated: %.2f KB\n", total/1024.0f);
+    snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0f);
     result.append(buffer);
     if (mAllocDev->common.version >= 1 && mAllocDev->dump) {
         mAllocDev->dump(mAllocDev, buffer, SIZE);
@@ -101,13 +107,19 @@
     if (err == NO_ERROR) {
         Mutex::Autolock _l(sLock);
         KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+        int bpp = bytesPerPixel(format);
+        if (bpp < 0) {
+            // probably a HAL custom format. in any case, we don't know
+            // what its pixel size is.
+            bpp = 0;
+        }
         alloc_rec_t rec;
         rec.w = w;
         rec.h = h;
         rec.s = *stride;
         rec.format = format;
         rec.usage = usage;
-        rec.size = h * stride[0] * bytesPerPixel(format);
+        rec.size = h * stride[0] * bpp;
         list.add(*handle, rec);
     }
 
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index a11fb80..3dd9249 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -201,11 +201,11 @@
 
 void MediaPlayer::disconnectNativeWindow() {
     if (mConnectedWindow != NULL) {
-        status_t err = native_window_disconnect(mConnectedWindow.get(),
+        status_t err = native_window_api_disconnect(mConnectedWindow.get(),
                 NATIVE_WINDOW_API_MEDIA);
 
         if (err != OK) {
-            LOGW("native_window_disconnect returned an error: %s (%d)",
+            LOGW("native_window_api_disconnect returned an error: %s (%d)",
                     strerror(-err), err);
         }
     }
@@ -224,7 +224,7 @@
     }
 
     if (surface != NULL) {
-        status_t err = native_window_connect(surface.get(),
+        status_t err = native_window_api_connect(surface.get(),
                 NATIVE_WINDOW_API_MEDIA);
 
         if (err != OK) {
@@ -274,7 +274,7 @@
     sp<ANativeWindow> anw;
     if (surfaceTexture != NULL) {
         anw = new SurfaceTextureClient(surfaceTexture);
-        status_t err = native_window_connect(anw.get(),
+        status_t err = native_window_api_connect(anw.get(),
                 NATIVE_WINDOW_API_MEDIA);
 
         if (err != OK) {
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index 020e947..8a42e8b 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -188,7 +188,7 @@
     }
 
     size_t endOffset = offset - 2;
-    while (data[endOffset - 1] == 0x00) {
+    while (endOffset > startOffset + 1 && data[endOffset - 1] == 0x00) {
         --endOffset;
     }
 
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 90d64ba..2578d2d 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -544,6 +544,7 @@
         firstSeqNumberInPlaylist = 0;
     }
 
+    bool seekDiscontinuity = false;
     bool explicitDiscontinuity = false;
     bool bandwidthChanged = false;
 
@@ -580,10 +581,10 @@
                     // reseting the data source will have had the
                     // side effect of discarding any previously queued
                     // bandwidth change discontinuity.
-                    // Therefore we'll need to treat these explicit
+                    // Therefore we'll need to treat these seek
                     // discontinuities as involving a bandwidth change
                     // even if they aren't directly.
-                    explicitDiscontinuity = true;
+                    seekDiscontinuity = true;
                     bandwidthChanged = true;
                 }
             }
@@ -597,11 +598,7 @@
     }
 
     if (mSeqNumber < 0) {
-        if (mPlaylist->isComplete()) {
-            mSeqNumber = firstSeqNumberInPlaylist;
-        } else {
-            mSeqNumber = firstSeqNumberInPlaylist + mPlaylist->size() / 2;
-        }
+        mSeqNumber = firstSeqNumberInPlaylist;
     }
 
     int32_t lastSeqNumberInPlaylist =
@@ -704,15 +701,17 @@
         bandwidthChanged = false;
     }
 
-    if (explicitDiscontinuity || bandwidthChanged) {
+    if (seekDiscontinuity || explicitDiscontinuity || bandwidthChanged) {
         // Signal discontinuity.
 
-        LOGI("queueing discontinuity (explicit=%d, bandwidthChanged=%d)",
-             explicitDiscontinuity, bandwidthChanged);
+        LOGI("queueing discontinuity (seek=%d, explicit=%d, bandwidthChanged=%d)",
+             seekDiscontinuity, explicitDiscontinuity, bandwidthChanged);
 
         sp<ABuffer> tmp = new ABuffer(188);
         memset(tmp->data(), 0, tmp->size());
-        tmp->data()[1] = bandwidthChanged;
+
+        // signal a 'hard' discontinuity for explicit or bandwidthChanged.
+        tmp->data()[1] = (explicitDiscontinuity || bandwidthChanged) ? 1 : 0;
 
         mDataSource->queueBuffer(tmp);
     }
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index ba5d29a..10cea22 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -363,6 +363,12 @@
         EGLConfig iConfig = dp->configs[intptr_t(config)].config;
         EGLint format;
 
+        if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != OK) {
+            LOGE("EGLNativeWindowType %p already connected to another API",
+                    window);
+            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+        }
+
         // set the native window's buffers format to match this config
         if (cnx->egl.eglGetConfigAttrib(iDpy,
                 iConfig, EGL_NATIVE_VISUAL_ID, &format)) {
@@ -371,6 +377,7 @@
                 if (err != 0) {
                     LOGE("error setting native window pixel format: %s (%d)",
                             strerror(-err), err);
+                    native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
                     return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
                 }
             }
@@ -383,6 +390,10 @@
                     dp->configs[intptr_t(config)].impl, cnx);
             return s;
         }
+
+        // EGLSurface creation failed
+        native_window_set_buffers_format(window, 0);
+        native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
     }
     return EGL_NO_SURFACE;
 }
@@ -443,8 +454,12 @@
     EGLBoolean result = s->cnx->egl.eglDestroySurface(
             dp->disp[s->impl].dpy, s->surface);
     if (result == EGL_TRUE) {
-        if (s->win != NULL) {
-            native_window_set_buffers_geometry(s->win.get(), 0, 0, 0);
+        ANativeWindow* const window = s->win.get();
+        if (window != NULL) {
+            native_window_set_buffers_format(window, 0);
+            if (native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL)) {
+                LOGE("EGLNativeWindowType %p disconnected failed", window);
+            }
         }
         _s.terminate();
     }
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 2c13069..c23a8a1 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -3,7 +3,7 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := $(call all-subdir-java-files) \
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../../../ex/carousel/java/com/android/ex/carousel/carousel.rs
 
 LOCAL_JAVA_LIBRARIES := services
@@ -16,3 +16,5 @@
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
 include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
new file mode 100644
index 0000000..28e4b86
--- /dev/null
+++ b/packages/SystemUI/tests/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2011 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_PACKAGE_NAME := SystemUITests
+
+# sign this with platform cert, so this test is allowed to inject key events into
+# UI it doesn't own. This is necessary to allow screenshots to be taken
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
new file mode 100644
index 0000000..e52806d
--- /dev/null
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.systemui.tests">
+
+    <uses-permission android:name="android.permission.INJECT_EVENTS" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="com.android.systemui.screenshot.ScreenshotStubActivity" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="com.android.systemui.tests"
+        android:label="Tests for SystemUI">
+    </instrumentation>
+</manifest>
diff --git a/packages/SystemUI/tests/res/layout/main.xml b/packages/SystemUI/tests/res/layout/main.xml
new file mode 100644
index 0000000..56dffe6
--- /dev/null
+++ b/packages/SystemUI/tests/res/layout/main.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+    <TextView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:text="this is a test activity"
+    />
+    <EditText
+        android:layout_height="wrap_content"
+        android:id="@+id/editText1"
+        android:layout_width="match_parent">
+        <requestFocus></requestFocus>
+    </EditText>
+</LinearLayout>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java
new file mode 100644
index 0000000..2935373
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 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 com.android.systemui.screenshot;
+
+import com.android.systemui.tests.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * A stub activity used in {@link ScreenshotTest}.
+ */
+public class ScreenshotStubActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotTest.java
new file mode 100644
index 0000000..a0bc4d7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011 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 com.android.systemui.screenshot;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Environment;
+import android.os.FileObserver;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import java.io.File;
+
+/**
+ * Functional tests for the global screenshot feature.
+ */
+@LargeTest
+public class ScreenshotTest extends ActivityInstrumentationTestCase2<ScreenshotStubActivity> {
+
+    private static final String LOG_TAG = "ScreenshotTest";
+    private static final int SCREEN_WAIT_TIME_SEC = 5;
+
+    public ScreenshotTest() {
+        super(ScreenshotStubActivity.class);
+    }
+
+    /**
+     * A simple test for screenshots that launches an Activity, injects the key event combo
+     * to trigger the screenshot, and verifies the screenshot was taken successfully.
+     */
+    public void testScreenshot() throws Exception {
+        Log.d(LOG_TAG, "starting testScreenshot");
+        // launch the activity.
+        ScreenshotStubActivity activity = getActivity();
+        assertNotNull(activity);
+
+        File screenshotDir = getScreenshotDir();
+        NewScreenshotObserver observer = new NewScreenshotObserver(
+                screenshotDir.getAbsolutePath());
+        observer.startWatching();
+        takeScreenshot();
+        // unlikely, but check if a new screenshot file was already created
+        if (observer.getCreatedPath() == null) {
+            // wait for screenshot to be created
+            synchronized(observer) {
+                observer.wait(SCREEN_WAIT_TIME_SEC*1000);
+            }
+        }
+        assertNotNull(String.format("Could not find screenshot after %d seconds",
+                SCREEN_WAIT_TIME_SEC), observer.getCreatedPath());
+
+        File screenshotFile = new File(screenshotDir, observer.getCreatedPath());
+        try {
+            assertTrue(String.format("Detected new screenshot %s but its not a file",
+                    screenshotFile.getName()), screenshotFile.isFile());
+            assertTrue(String.format("Detected new screenshot %s but its not an image",
+                    screenshotFile.getName()), isValidImage(screenshotFile));
+        } finally {
+            // delete the file to prevent external storage from filing up
+            screenshotFile.delete();
+        }
+    }
+
+    private static class NewScreenshotObserver extends FileObserver {
+        private String mAddedPath = null;
+
+        NewScreenshotObserver(String path) {
+            super(path, FileObserver.CREATE);
+        }
+
+        synchronized String getCreatedPath() {
+            return mAddedPath;
+        }
+
+        @Override
+        public void onEvent(int event, String path) {
+            Log.d(LOG_TAG, String.format("Detected new file added %s", path));
+            synchronized (this) {
+                mAddedPath = path;
+                notify();
+            }
+        }
+    }
+
+    /**
+     * Inject the key sequence to take a screenshot.
+     */
+    private void takeScreenshot() {
+        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_POWER));
+        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_VOLUME_DOWN));
+        // the volume down key event will cause the 'volume adjustment' UI to appear in the
+        // foreground, and steal UI focus
+        // unfortunately this means the next key event will get directed to the
+        // 'volume adjustment' UI, instead of this test's activity
+        // for this reason this test must be signed with platform certificate, to grant this test
+        // permission to inject key events to another process
+        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_VOLUME_DOWN));
+        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_POWER));
+    }
+
+    /**
+     * Get the directory where screenshot images are stored.
+     */
+    private File getScreenshotDir() {
+        // TODO: get this dir location from a constant
+        return new File(Environment.getExternalStorageDirectory(), "Pictures" + File.separator +
+                "Screenshots");
+    }
+
+    /**
+     * Return true if file is valid image file
+     */
+    private boolean isValidImage(File screenshotFile) {
+        Bitmap b = BitmapFactory.decodeFile(screenshotFile.getAbsolutePath());
+        // TODO: do more checks on image
+        return b != null;
+    }
+}
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index b03649e..def25d1 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -460,10 +460,10 @@
 
 static void disconnectWindow(const sp<ANativeWindow>& window) {
     if (window != 0) {
-        status_t result = native_window_disconnect(window.get(),
+        status_t result = native_window_api_disconnect(window.get(),
                 NATIVE_WINDOW_API_CAMERA);
         if (result != NO_ERROR) {
-            LOGW("native_window_disconnect failed: %s (%d)", strerror(-result),
+            LOGW("native_window_api_disconnect failed: %s (%d)", strerror(-result),
                     result);
         }
     }
@@ -526,9 +526,9 @@
     }
 
     if (window != 0) {
-        result = native_window_connect(window.get(), NATIVE_WINDOW_API_CAMERA);
+        result = native_window_api_connect(window.get(), NATIVE_WINDOW_API_CAMERA);
         if (result != NO_ERROR) {
-            LOGE("native_window_connect failed: %s (%d)", strerror(-result),
+            LOGE("native_window_api_connect failed: %s (%d)", strerror(-result),
                     result);
             return result;
         }
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 39d2b1c..a59b6c0 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -21,6 +21,8 @@
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 import static android.provider.Settings.Secure.NETSTATS_ENABLED;
+import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
+import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -54,7 +56,6 @@
 import java.io.InputStreamReader;
 import java.net.Inet4Address;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -89,8 +90,10 @@
     private static final String KEY_IFACE = "iface";
     private static final String KEY_TAG_HEX = "acct_tag_hex";
     private static final String KEY_UID = "uid_tag_int";
-    private static final String KEY_RX = "rx_bytes";
-    private static final String KEY_TX = "tx_bytes";
+    private static final String KEY_RX_BYTES = "rx_bytes";
+    private static final String KEY_RX_PACKETS = "rx_packets";
+    private static final String KEY_TX_BYTES = "tx_bytes";
+    private static final String KEY_TX_PACKETS = "tx_packets";
 
     class NetdResponseCode {
         /* Keep in sync with system/netd/ResponseCode.h */
@@ -203,8 +206,7 @@
             Slog.d(TAG, "not enabling bandwidth control");
         }
 
-        SystemProperties.set(NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED,
-                mBandwidthControlEnabled ? "1" : "0");
+        SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0");
     }
 
     public void registerObserver(INetworkManagementEventObserver obs) {
@@ -1249,6 +1251,9 @@
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
         final NetworkStats.Entry entry = new NetworkStats.Entry();
 
+        // TODO: remove knownLines check once 5087722 verified
+        final HashSet<String> knownLines = Sets.newHashSet();
+
         final ArrayList<String> keys = Lists.newArrayList();
         final ArrayList<String> values = Lists.newArrayList();
         final HashMap<String, String> parsed = Maps.newHashMap();
@@ -1266,14 +1271,18 @@
                 splitLine(line, values);
                 parseLine(keys, values, parsed);
 
+                if (!knownLines.add(line)) {
+                    throw new IllegalStateException("encountered duplicate proc entry");
+                }
+
                 try {
-                    // TODO: add rxPackets/txPackets once kernel exports
                     entry.iface = parsed.get(KEY_IFACE);
-                    entry.tag = NetworkManagementSocketTagger.kernelToTag(
-                            parsed.get(KEY_TAG_HEX));
-                    entry.uid = Integer.parseInt(parsed.get(KEY_UID));
-                    entry.rxBytes = Long.parseLong(parsed.get(KEY_RX));
-                    entry.txBytes = Long.parseLong(parsed.get(KEY_TX));
+                    entry.tag = kernelToTag(parsed.get(KEY_TAG_HEX));
+                    entry.uid = getParsedInt(parsed, KEY_UID);
+                    entry.rxBytes = getParsedLong(parsed, KEY_RX_BYTES);
+                    entry.rxPackets = getParsedLong(parsed, KEY_RX_PACKETS);
+                    entry.txBytes = getParsedLong(parsed, KEY_TX_BYTES);
+                    entry.txPackets = getParsedLong(parsed, KEY_TX_PACKETS);
 
                     if (limitUid == UID_ALL || limitUid == entry.uid) {
                         stats.addValues(entry);
@@ -1291,6 +1300,16 @@
         return stats;
     }
 
+    private static int getParsedInt(HashMap<String, String> parsed, String key) {
+        final String value = parsed.get(key);
+        return value != null ? Integer.parseInt(value) : 0;
+    }
+
+    private static long getParsedLong(HashMap<String, String> parsed, String key) {
+        final String value = parsed.get(key);
+        return value != null ? Long.parseLong(value) : 0;
+    }
+
     /**
      * Build {@link NetworkStats} with detailed UID statistics.
      *
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 6cc01f4..24188ca 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -17,6 +17,8 @@
 package com.android.server.net;
 
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.MODIFY_NETWORK_ACCOUNTING;
 import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.content.Intent.ACTION_SHUTDOWN;
@@ -56,6 +58,7 @@
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
+import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -65,6 +68,7 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
+import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.NtpTrustedTime;
 import android.util.Slog;
@@ -150,9 +154,9 @@
 
     /** Set of currently active ifaces. */
     private HashMap<String, NetworkIdentitySet> mActiveIfaces = Maps.newHashMap();
-    /** Set of historical stats for known networks. */
+    /** Set of historical network layer stats for known networks. */
     private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkStats = Maps.newHashMap();
-    /** Set of historical stats for known UIDs. */
+    /** Set of historical network layer stats for known UIDs. */
     private HashMap<NetworkIdentitySet, LongSparseArray<NetworkStatsHistory>> mUidStats =
             Maps.newHashMap();
 
@@ -164,6 +168,10 @@
 
     private NetworkStats mLastUidSnapshot;
 
+    /** Data layer operation counters for splicing into other structures. */
+    private NetworkStats mOperations = new NetworkStats(0L, 10);
+    private NetworkStats mLastOperationsSnapshot;
+
     private final HandlerThread mHandlerThread;
     private final Handler mHandler;
 
@@ -381,9 +389,13 @@
                             entry.uid = uid;
                             entry.tag = tag;
                             entry.rxBytes = historyEntry.rxBytes;
+                            entry.rxPackets = historyEntry.rxPackets;
                             entry.txBytes = historyEntry.txBytes;
+                            entry.txPackets = historyEntry.txPackets;
+                            entry.operations = historyEntry.operations;
 
-                            if (entry.rxBytes > 0 || entry.txBytes > 0) {
+                            if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
+                                    || entry.txPackets > 0 || entry.operations > 0) {
                                 stats.combineValues(entry);
                             }
                         }
@@ -396,6 +408,41 @@
     }
 
     @Override
+    public NetworkStats getDataLayerSnapshotForUid(int uid) throws RemoteException {
+        if (Binder.getCallingUid() != uid) {
+            mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
+        }
+
+        // TODO: switch to data layer stats once kernel exports
+        // for now, read network layer stats and flatten across all ifaces
+        final NetworkStats networkLayer = mNetworkManager.getNetworkStatsUidDetail(uid);
+        final NetworkStats dataLayer = new NetworkStats(
+                networkLayer.getElapsedRealtime(), networkLayer.size());
+
+        NetworkStats.Entry entry = null;
+        for (int i = 0; i < networkLayer.size(); i++) {
+            entry = networkLayer.getValues(i, entry);
+            entry.iface = IFACE_ALL;
+            dataLayer.combineValues(entry);
+        }
+
+        // splice in operation counts
+        dataLayer.spliceOperationsFrom(mOperations);
+        return dataLayer;
+    }
+
+    @Override
+    public void incrementOperationCount(int uid, int tag, int operationCount) {
+        if (Binder.getCallingUid() != uid) {
+            mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
+        }
+
+        synchronized (mStatsLock) {
+            mOperations.combineValues(IFACE_ALL, uid, tag, 0L, 0L, 0L, 0L, operationCount);
+        }
+    }
+
+    @Override
     public void forceUpdate() {
         mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
 
@@ -533,7 +580,7 @@
         final NetworkStats uidSnapshot;
         try {
             networkSnapshot = mNetworkManager.getNetworkStatsSummary();
-            uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null;
+            uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsUidDetail(UID_ALL) : null;
         } catch (IllegalStateException e) {
             Slog.w(TAG, "problem reading network stats: " + e);
             return;
@@ -592,7 +639,7 @@
             }
 
             final NetworkStatsHistory history = findOrCreateNetworkStatsLocked(ident);
-            history.recordData(timeStart, currentTime, entry.rxBytes, entry.txBytes);
+            history.recordData(timeStart, currentTime, entry);
         }
 
         // trim any history beyond max
@@ -615,9 +662,12 @@
         ensureUidStatsLoadedLocked();
 
         final NetworkStats delta = computeStatsDelta(mLastUidSnapshot, uidSnapshot);
+        final NetworkStats operationsDelta = computeStatsDelta(
+                mLastOperationsSnapshot, mOperations);
         final long timeStart = currentTime - delta.getElapsedRealtime();
 
         NetworkStats.Entry entry = null;
+        NetworkStats.Entry operationsEntry = null;
         for (int i = 0; i < delta.size(); i++) {
             entry = delta.getValues(i, entry);
             final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
@@ -625,9 +675,16 @@
                 continue;
             }
 
+            // splice in operation counts since last poll
+            final int j = operationsDelta.findIndex(IFACE_ALL, entry.uid, entry.tag);
+            if (j != -1) {
+                operationsEntry = operationsDelta.getValues(j, operationsEntry);
+                entry.operations = operationsEntry.operations;
+            }
+
             final NetworkStatsHistory history = findOrCreateUidStatsLocked(
                     ident, entry.uid, entry.tag);
-            history.recordData(timeStart, currentTime, entry.rxBytes, entry.txBytes);
+            history.recordData(timeStart, currentTime, entry);
         }
 
         // trim any history beyond max
@@ -648,6 +705,8 @@
         }
 
         mLastUidSnapshot = uidSnapshot;
+        mLastOperationsSnapshot = mOperations;
+        mOperations = new NetworkStats(0L, 10);
     }
 
     /**
@@ -980,7 +1039,7 @@
                         final int tag = unpackTag(packed);
                         final NetworkStatsHistory history = uidStats.valueAt(i);
                         pw.print("    UID="); pw.print(uid);
-                        pw.print(" tag="); pw.println(tag);
+                        pw.print(" tag=0x"); pw.println(Integer.toHexString(tag));
                         history.dump("    ", pw, fullHistory);
                     }
                 }
@@ -1028,7 +1087,9 @@
         if (before != null) {
             return current.subtractClamped(before);
         } else {
-            return current;
+            // this is first snapshot; to prevent from double-counting we only
+            // observe traffic occuring between known snapshots.
+            return new NetworkStats(0L, 10);
         }
     }
 
@@ -1114,5 +1175,4 @@
             return DAY_IN_MILLIS;
         }
     }
-
 }
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index f67c82e..b178e49 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -22,7 +22,7 @@
 	LOCAL_CFLAGS += -DNO_RGBX_8888
 endif
 ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
-	LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
+	LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE
 endif
 
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 63062cc..886bb2a 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -105,6 +105,7 @@
 // in the purgatory list
 void Layer::onRemoved()
 {
+    mSurfaceTexture->abandon();
 }
 
 sp<ISurface> Layer::createSurface()
diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp
index 91e010f..5973e76 100644
--- a/services/surfaceflinger/SurfaceTextureLayer.cpp
+++ b/services/surfaceflinger/SurfaceTextureLayer.cpp
@@ -86,6 +86,32 @@
     return res;
 }
 
+status_t SurfaceTextureLayer::connect(int api) {
+    status_t err = SurfaceTexture::connect(api);
+    if (err == NO_ERROR) {
+        switch(api) {
+            case NATIVE_WINDOW_API_MEDIA:
+            case NATIVE_WINDOW_API_CAMERA:
+                // Camera preview and videos are rate-limited on the producer
+                // side.  If enabled for this build, we use async mode to always
+                // show the most recent frame at the cost of requiring an
+                // additional buffer.
+#ifndef NEVER_DEFAULT_TO_ASYNC_MODE
+                err = setSynchronousMode(false);
+                break;
+#endif
+                // fall through to set synchronous mode when not defaulting to
+                // async mode.
+            deafult:
+                err = setSynchronousMode(true);
+                break;
+        }
+        if (err != NO_ERROR) {
+            disconnect(api);
+        }
+    }
+    return err;
+}
 
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/surfaceflinger/SurfaceTextureLayer.h b/services/surfaceflinger/SurfaceTextureLayer.h
index 29a9cbe..5d328b7 100644
--- a/services/surfaceflinger/SurfaceTextureLayer.h
+++ b/services/surfaceflinger/SurfaceTextureLayer.h
@@ -50,6 +50,8 @@
 
     virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h,
             uint32_t format, uint32_t usage);
+
+    virtual status_t connect(int api);
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 1620405..60be35a 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -26,10 +26,12 @@
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+    <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
index 56ef995a..f628977 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
@@ -50,10 +50,9 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        final File canonicalFilesDir = getContext().getFilesDir().getCanonicalFile();
-        mTestProc = new File(canonicalFilesDir, "proc");
+        mTestProc = new File(getContext().getFilesDir(), "proc");
         if (mTestProc.exists()) {
-            Files.deleteRecursively(mTestProc);
+            IoUtils.deleteContents(mTestProc);
         }
 
         mService = NetworkManagementService.createForTest(mContext, mTestProc, true);
@@ -64,7 +63,7 @@
         mService = null;
 
         if (mTestProc.exists()) {
-            Files.deleteRecursively(mTestProc);
+            IoUtils.deleteContents(mTestProc);
         }
 
         super.tearDown();
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index aab09ca..91fef22 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -197,9 +197,6 @@
         expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
         expectTime(System.currentTimeMillis());
 
-        // default behavior is background data enabled
-        expect(mConnManager.getBackgroundDataSetting()).andReturn(true);
-
         replay();
         mService.systemReady();
         verifyAndReset();
@@ -471,7 +468,7 @@
 
         // pretend that 512 bytes total have happened
         stats = new NetworkStats(elapsedRealtime, 1)
-                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 256L, 2L, 256L, 2L);
+                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 256L, 2L, 256L, 2L, 11);
         expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
                 .andReturn(stats).atLeastOnce();
 
@@ -547,7 +544,7 @@
         elapsedRealtime += MINUTE_IN_MILLIS;
         currentTime = TIME_MAR_10 + elapsedRealtime;
         stats = new NetworkStats(elapsedRealtime, 1)
-                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L);
+                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0);
         state = new NetworkState[] { buildWifi() };
 
         {
@@ -574,7 +571,7 @@
         elapsedRealtime += MINUTE_IN_MILLIS;
         currentTime = TIME_MAR_10 + elapsedRealtime;
         stats = new NetworkStats(elapsedRealtime, 1)
-                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 1536L, 15L, 0L, 0L);
+                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 1536L, 15L, 0L, 0L, 11);
 
         {
             expectTime(currentTime);
@@ -595,7 +592,7 @@
         elapsedRealtime += MINUTE_IN_MILLIS;
         currentTime = TIME_MAR_10 + elapsedRealtime;
         stats = new NetworkStats(elapsedRealtime, 1)
-                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 5120L, 512L, 0L, 0L);
+                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 5120L, 512L, 0L, 0L, 22);
 
         {
             expectTime(currentTime);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index bd80af9..cf69fd5 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -160,22 +160,25 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
 
         // verify service has empty history for wifi
-        assertNetworkTotal(sTemplateWifi, 0L, 0L);
+        assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // modify some number on wifi, and trigger poll event
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 1024L, 1L, 2048L, 2L));
-        expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertNetworkTotal(sTemplateWifi, 1024L, 2048L);
+        assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0);
         verifyAndReset();
 
         // and bump forward again, with counters going higher. this is
@@ -185,13 +188,13 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 4096L, 4L, 8192L, 8L));
-        expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertNetworkTotal(sTemplateWifi, 4096L, 8192L);
+        assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0);
         verifyAndReset();
 
     }
@@ -211,26 +214,32 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
 
         // verify service has empty history for wifi
-        assertNetworkTotal(sTemplateWifi, 0L, 0L);
+        assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // modify some number on wifi, and trigger poll event
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 1024L, 8L, 2048L, 16L));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 2)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 2)
                 .addValues(TEST_IFACE, UID_RED, TAG_NONE, 512L, 4L, 256L, 2L)
                 .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 128L, 1L, 128L, 1L));
 
+        mService.incrementOperationCount(UID_RED, TAG_NONE, 20);
+        mService.incrementOperationCount(UID_BLUE, TAG_NONE, 10);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertNetworkTotal(sTemplateWifi, 1024L, 2048L);
-        assertUidTotal(sTemplateWifi, UID_RED, 512L, 256L);
-        assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 128L);
+        assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
+        assertUidTotal(sTemplateWifi, UID_RED, 512L, 4L, 256L, 2L, 20);
+        assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 10);
         verifyAndReset();
 
         // graceful shutdown system, which should trigger persist of stats, and
@@ -241,7 +250,7 @@
         // we persisted them to file.
         expectDefaultSettings();
         replay();
-        assertNetworkTotal(sTemplateWifi, 0L, 0L);
+        assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
         verifyAndReset();
 
         assertStatsFilesExist(true);
@@ -254,9 +263,9 @@
         mService.systemReady();
 
         // after systemReady(), we should have historical stats loaded again
-        assertNetworkTotal(sTemplateWifi, 1024L, 2048L);
-        assertUidTotal(sTemplateWifi, UID_RED, 512L, 256L);
-        assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 128L);
+        assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
+        assertUidTotal(sTemplateWifi, UID_RED, 512L, 4L, 256L, 2L, 20);
+        assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 10);
         verifyAndReset();
 
     }
@@ -278,20 +287,23 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // modify some number on wifi, and trigger poll event
         elapsedRealtime += 2 * HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 512L, 4L, 512L, 4L));
-        expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
         history = mService.getHistoryForNetwork(sTemplateWifi);
-        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 512L);
+        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0);
         assertEquals(HOUR_IN_MILLIS, history.getBucketDuration());
         assertEquals(2, history.size());
         verifyAndReset();
@@ -301,14 +313,14 @@
         expectTime(TEST_START + elapsedRealtime);
         expectSettings(0L, 30 * MINUTE_IN_MILLIS, WEEK_IN_MILLIS);
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify identical stats, but spread across 4 buckets now
         history = mService.getHistoryForNetwork(sTemplateWifi);
-        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 512L);
+        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0);
         assertEquals(30 * MINUTE_IN_MILLIS, history.getBucketDuration());
         assertEquals(4, history.size());
         verifyAndReset();
@@ -328,25 +340,32 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // create some traffic on first network
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 2048L, 16L, 512L, 4L));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 3)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 3)
                 .addValues(TEST_IFACE, UID_RED, TAG_NONE, 1536L, 12L, 512L, 4L)
                 .addValues(TEST_IFACE, UID_RED, 0xF00D, 512L, 4L, 512L, 4L)
                 .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 512L, 4L, 0L, 0L));
 
+        mService.incrementOperationCount(UID_RED, TAG_NONE, 15);
+        mService.incrementOperationCount(UID_RED, 0xF00D, 10);
+        mService.incrementOperationCount(UID_BLUE, TAG_NONE, 5);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertNetworkTotal(sTemplateImsi1, 2048L, 512L);
-        assertNetworkTotal(sTemplateWifi, 0L, 0L);
-        assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 512L);
-        assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 0L);
+        assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0);
+        assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
+        assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 512L, 4L, 15);
+        assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 4L, 0L, 0L, 5);
         verifyAndReset();
 
         // now switch networks; this also tests that we're okay with interfaces
@@ -356,7 +375,7 @@
         expectDefaultSettings();
         expectNetworkState(buildMobile3gState(IMSI_2));
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
 
         replay();
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
@@ -369,22 +388,24 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 128L, 1L, 1024L, 8L));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 128L, 1L, 1024L, 8L));
 
+        mService.incrementOperationCount(UID_BLUE, TAG_NONE, 10);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify original history still intact
-        assertNetworkTotal(sTemplateImsi1, 2048L, 512L);
-        assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 512L);
-        assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 0L);
+        assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0);
+        assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 512L, 4L, 15);
+        assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 4L, 0L, 0L, 5);
 
         // and verify new history also recorded under different template, which
         // verifies that we didn't cross the streams.
-        assertNetworkTotal(sTemplateImsi2, 128L, 1024L);
-        assertNetworkTotal(sTemplateWifi, 0L, 0L);
-        assertUidTotal(sTemplateImsi2, UID_BLUE, 128L, 1024L);
+        assertNetworkTotal(sTemplateImsi2, 128L, 1L, 1024L, 8L, 0);
+        assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
+        assertUidTotal(sTemplateImsi2, UID_BLUE, 128L, 1L, 1024L, 8L, 10);
         verifyAndReset();
 
     }
@@ -402,25 +423,32 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // create some traffic
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 4128L, 258L, 544L, 34L));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_RED, TAG_NONE, 16L, 1L, 16L, 1L)
                 .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 4096L, 258L, 512L, 32L)
                 .addValues(TEST_IFACE, UID_GREEN, TAG_NONE, 16L, 1L, 16L, 1L));
 
+        mService.incrementOperationCount(UID_RED, TAG_NONE, 10);
+        mService.incrementOperationCount(UID_BLUE, TAG_NONE, 15);
+        mService.incrementOperationCount(UID_GREEN, TAG_NONE, 5);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertNetworkTotal(sTemplateWifi, 4128L, 544L);
-        assertUidTotal(sTemplateWifi, UID_RED, 16L, 16L);
-        assertUidTotal(sTemplateWifi, UID_BLUE, 4096L, 512L);
-        assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 16L);
+        assertNetworkTotal(sTemplateWifi, 4128L, 258L, 544L, 34L, 0);
+        assertUidTotal(sTemplateWifi, UID_RED, 16L, 1L, 16L, 1L, 10);
+        assertUidTotal(sTemplateWifi, UID_BLUE, 4096L, 258L, 512L, 32L, 15);
+        assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 1L, 16L, 1L, 5);
         verifyAndReset();
 
         // now pretend two UIDs are uninstalled, which should migrate stats to
@@ -435,11 +463,11 @@
 
         // existing uid and total should remain unchanged; but removed UID
         // should be gone completely.
-        assertNetworkTotal(sTemplateWifi, 4128L, 544L);
-        assertUidTotal(sTemplateWifi, UID_RED, 0L, 0L);
-        assertUidTotal(sTemplateWifi, UID_BLUE, 0L, 0L);
-        assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 16L);
-        assertUidTotal(sTemplateWifi, UID_REMOVED, 4112L, 528L);
+        assertNetworkTotal(sTemplateWifi, 4128L, 258L, 544L, 34L, 0);
+        assertUidTotal(sTemplateWifi, UID_RED, 0L, 0L, 0L, 0L, 0);
+        assertUidTotal(sTemplateWifi, UID_BLUE, 0L, 0L, 0L, 0L, 0);
+        assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 1L, 16L, 1L, 5);
+        assertUidTotal(sTemplateWifi, UID_REMOVED, 4112L, 259L, 528L, 33L, 25);
         verifyAndReset();
 
     }
@@ -457,20 +485,26 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // create some traffic
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_RED, TAG_NONE, 1024L, 8L, 1024L, 8L)
                 .addValues(TEST_IFACE, UID_RED, 0xF00D, 512L, 4L, 512L, 4L));
 
+        mService.incrementOperationCount(UID_RED, TAG_NONE, 10);
+        mService.incrementOperationCount(UID_RED, 0xF00D, 5);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 1024L);
+        assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 10);
         verifyAndReset();
 
         // now switch over to 4g network
@@ -479,7 +513,7 @@
         expectDefaultSettings();
         expectNetworkState(buildMobile4gState());
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
 
         replay();
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
@@ -491,14 +525,16 @@
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_RED, TAG_NONE, 512L, 4L, 256L, 2L));
 
+        mService.incrementOperationCount(UID_RED, TAG_NONE, 5);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify that ALL_MOBILE template combines both
-        assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 1280L);
+        assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 1280L, 10L, 15);
 
         verifyAndReset();
 
@@ -537,32 +573,41 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // create some traffic for two apps
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_RED, TAG_NONE, 50L, 5L, 50L, 5L)
                 .addValues(TEST_IFACE, UID_RED, 0xF00D, 10L, 1L, 10L, 1L)
                 .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 1024L, 8L, 512L, 4L));
 
+        mService.incrementOperationCount(UID_RED, TAG_NONE, 5);
+        mService.incrementOperationCount(UID_RED, 0xF00D, 1);
+        mService.incrementOperationCount(UID_BLUE, TAG_NONE, 10);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertUidTotal(sTemplateWifi, UID_RED, 50L, 50L);
-        assertUidTotal(sTemplateWifi, UID_BLUE, 1024L, 512L);
+        assertUidTotal(sTemplateWifi, UID_RED, 50L, 5L, 50L, 5L, 5);
+        assertUidTotal(sTemplateWifi, UID_BLUE, 1024L, 8L, 512L, 4L, 10);
         verifyAndReset();
-        
+
         // now create more traffic in next hour, but only for one app
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 2048L, 16L, 1024L, 8L));
 
+        mService.incrementOperationCount(UID_BLUE, TAG_NONE, 15);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
@@ -570,28 +615,32 @@
         NetworkStats stats = mService.getSummaryForAllUid(
                 sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
         assertEquals(3, stats.size());
-        assertValues(stats, 0, IFACE_ALL, UID_RED, TAG_NONE, 50L, 5L, 50L, 5L);
-        assertValues(stats, 1, IFACE_ALL, UID_RED, 0xF00D, 10L, 1L, 10L, 1L);
-        assertValues(stats, 2, IFACE_ALL, UID_BLUE, TAG_NONE, 2048L, 16L, 1024L, 8L);
+        assertValues(stats, 0, IFACE_ALL, UID_RED, TAG_NONE, 50L, 5L, 50L, 5L, 5);
+        assertValues(stats, 1, IFACE_ALL, UID_RED, 0xF00D, 10L, 1L, 10L, 1L, 1);
+        assertValues(stats, 2, IFACE_ALL, UID_BLUE, TAG_NONE, 2048L, 16L, 1024L, 8L, 15);
 
         // now verify that recent history only contains one uid
         final long currentTime = TEST_START + elapsedRealtime;
         stats = mService.getSummaryForAllUid(
                 sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true);
         assertEquals(1, stats.size());
-        assertValues(stats, 0, IFACE_ALL, UID_BLUE, TAG_NONE, 1024L, 8L, 512L, 4L);
+        assertValues(stats, 0, IFACE_ALL, UID_BLUE, TAG_NONE, 1024L, 8L, 512L, 4L, 5);
 
         verifyAndReset();
     }
 
-    private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long txBytes) {
+    private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long rxPackets,
+            long txBytes, long txPackets, int operations) {
         final NetworkStatsHistory history = mService.getHistoryForNetwork(template);
-        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, txBytes);
+        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes,
+                txPackets, operations);
     }
 
-    private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long txBytes) {
+    private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long rxPackets,
+            long txBytes, long txPackets, int operations) {
         final NetworkStatsHistory history = mService.getHistoryForUid(template, uid, TAG_NONE);
-        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, txBytes);
+        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes,
+                txPackets, operations);
     }
 
     private void expectSystemReady() throws Exception {
@@ -611,8 +660,8 @@
         expect(mNetManager.getNetworkStatsSummary()).andReturn(summary).atLeastOnce();
     }
 
-    private void expectNetworkStatsDetail(NetworkStats detail) throws Exception {
-        expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
+    private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception {
+        expect(mNetManager.getNetworkStatsUidDetail(eq(UID_ALL))).andReturn(detail).atLeastOnce();
     }
 
     private void expectDefaultSettings() throws Exception {
@@ -639,6 +688,17 @@
         expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
     }
 
+    private void performBootstrapPoll(long testStart, long elapsedRealtime) throws Exception {
+        expectTime(testStart + elapsedRealtime);
+        expectDefaultSettings();
+        expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
+
+        replay();
+        mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+        verifyAndReset();
+    }
+
     private void assertStatsFilesExist(boolean exist) {
         final File networkFile = new File(mStatsDir, "netstats.bin");
         final File uidFile = new File(mStatsDir, "netstats_uid.bin");
@@ -652,23 +712,26 @@
     }
 
     private static void assertValues(NetworkStats stats, int i, String iface, int uid, int tag,
-            long rxBytes, long rxPackets, long txBytes, long txPackets) {
+            long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) {
         final NetworkStats.Entry entry = stats.getValues(i, null);
-        assertEquals(iface, entry.iface);
-        assertEquals(uid, entry.uid);
-        assertEquals(tag, entry.tag);
-        assertEquals(rxBytes, entry.rxBytes);
-        // TODO: enable testing packet counts once stored in history
-//        assertEquals(rxPackets, entry.rxPackets);
-        assertEquals(txBytes, entry.txBytes);
-//        assertEquals(txPackets, entry.txPackets);
+        assertEquals("unexpected iface", iface, entry.iface);
+        assertEquals("unexpected uid", uid, entry.uid);
+        assertEquals("unexpected tag", tag, entry.tag);
+        assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
+        assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
+        assertEquals("unexpected txBytes", txBytes, entry.txBytes);
+        assertEquals("unexpected txPackets", txPackets, entry.txPackets);
+        assertEquals("unexpected operations", operations, entry.operations);
     }
 
-    private static void assertValues(
-            NetworkStatsHistory stats, long start, long end, long rxBytes, long txBytes) {
+    private static void assertValues(NetworkStatsHistory stats, long start, long end, long rxBytes,
+            long rxPackets, long txBytes, long txPackets, int operations) {
         final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null);
         assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
+        assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
+        assertEquals("unexpected txPackets", txPackets, entry.txPackets);
+        assertEquals("unexpected operations", operations, entry.operations);
     }
 
     private static NetworkState buildWifiState() {
diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
index 50c18f0..c0870c7 100644
--- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
@@ -289,7 +289,7 @@
     public void expectGetInterfaceCounter(long rx, long tx) throws Exception {
         // TODO: provide elapsedRealtime mock to match TimeAuthority
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
-        stats.addValues(TEST_IFACE, NetworkStats.UID_ALL, NetworkStats.TAG_NONE, rx, 0L, tx, 0L);
+        stats.addValues(TEST_IFACE, NetworkStats.UID_ALL, NetworkStats.TAG_NONE, rx, 0L, tx, 0L, 0);
 
         expect(mMockNMService.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
     }
diff --git a/test-runner/src/android/test/ActivityInstrumentationTestCase2.java b/test-runner/src/android/test/ActivityInstrumentationTestCase2.java
index e8570bd..24b125e 100644
--- a/test-runner/src/android/test/ActivityInstrumentationTestCase2.java
+++ b/test-runner/src/android/test/ActivityInstrumentationTestCase2.java
@@ -147,8 +147,8 @@
     protected void setUp() throws Exception {
         super.setUp();
         
-        boolean mInitialTouchMode = false;
-        Intent mActivityIntent = null;
+        mInitialTouchMode = false;
+        mActivityIntent = null;
     }
 
     @Override