Merge "Use ArrayList instead of ArraySet for FillResponse.getDatasets()..."
diff --git a/Android.mk b/Android.mk
index b26543d..b98d3bc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -109,6 +109,7 @@
 	core/java/android/app/backup/IRestoreObserver.aidl \
 	core/java/android/app/backup/IRestoreSession.aidl \
 	core/java/android/app/backup/ISelectBackupTransportCallback.aidl \
+	core/java/android/app/usage/ICacheQuotaService.aidl \
 	core/java/android/app/usage/IStorageStatsManager.aidl \
 	core/java/android/app/usage/IUsageStatsManager.aidl \
 	core/java/android/bluetooth/IBluetooth.aidl \
@@ -322,6 +323,7 @@
 	core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \
 	core/java/android/view/IDockedStackListener.aidl \
 	core/java/android/view/IGraphicsStats.aidl \
+	core/java/android/view/IGraphicsStatsCallback.aidl \
 	core/java/android/view/IInputFilter.aidl \
 	core/java/android/view/IInputFilterHost.aidl \
 	core/java/android/view/IOnKeyguardExitResult.aidl \
@@ -708,6 +710,7 @@
 	frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \
 	frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \
 	frameworks/base/core/java/android/speech/tts/Voice.aidl \
+	frameworks/base/core/java/android/app/usage/CacheQuotaHint.aidl \
 	frameworks/base/core/java/android/app/usage/ExternalStorageStats.aidl \
 	frameworks/base/core/java/android/app/usage/StorageStats.aidl \
 	frameworks/base/core/java/android/app/usage/UsageEvents.aidl \
@@ -1421,6 +1424,24 @@
 
 include $(BUILD_JAVA_LIBRARY)
 
+# ====  c++ proto device library  ==============================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libplatformprotos
+# b/34740546, work around clang-tidy segmentation fault.
+LOCAL_TIDY_CHECKS := -modernize*
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+LOCAL_PROTOC_FLAGS := \
+    --include_source_info \
+    -Iexternal/protobuf/src
+LOCAL_SRC_FILES := \
+    $(call all-proto-files-under, core/proto) \
+    $(call all-proto-files-under, libs/incident/proto)
+LOCAL_C_INCLUDES := \
+    $(call generated-sources-dir-for,STATIC_LIBRARIES,libplatformprotos,)/proto
+LOCAL_EXPORT_C_INCLUDES := \
+    $(call generated-sources-dir-for,STATIC_LIBRARIES,libplatformprotos,)/proto
+include $(BUILD_STATIC_LIBRARY)
+
 # ====  c++ proto host library  ==============================
 include $(CLEAR_VARS)
 LOCAL_MODULE := libplatformprotos
diff --git a/api/current.txt b/api/current.txt
index 85e153a..67cdc9c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31255,10 +31255,8 @@
     method public void allocateBytes(java.io.File, long, int) throws java.io.IOException;
     method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException;
     method public long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
-    method public long getCacheQuotaBytes();
-    method public long getCacheSizeBytes();
-    method public long getExternalCacheQuotaBytes();
-    method public long getExternalCacheSizeBytes();
+    method public long getCacheQuotaBytes(java.io.File);
+    method public long getCacheSizeBytes(java.io.File);
     method public java.lang.String getMountedObbPath(java.lang.String);
     method public android.os.storage.StorageVolume getPrimaryStorageVolume();
     method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
@@ -45639,6 +45637,7 @@
     method public abstract void setHint(java.lang.CharSequence);
     method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String);
     method public abstract void setLongClickable(boolean);
+    method public abstract void setSanitized(boolean);
     method public abstract void setSelected(boolean);
     method public abstract void setText(java.lang.CharSequence);
     method public abstract void setText(java.lang.CharSequence, int, int);
@@ -45647,7 +45646,6 @@
     method public abstract void setTransformation(android.graphics.Matrix);
     method public abstract void setUrl(java.lang.String);
     method public abstract void setVisibility(int);
-    field public static final int AUTO_FILL_FLAG_SANITIZED = 1; // 0x1
   }
 
   public final class ViewStub extends android.view.View {
diff --git a/api/removed.txt b/api/removed.txt
index ab22b6e..e467811 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -180,6 +180,10 @@
 package android.os.storage {
 
   public class StorageManager {
+    method public deprecated long getCacheQuotaBytes();
+    method public deprecated long getCacheSizeBytes();
+    method public deprecated long getExternalCacheQuotaBytes();
+    method public deprecated long getExternalCacheSizeBytes();
     method public android.os.storage.StorageVolume getPrimaryVolume();
     method public android.os.storage.StorageVolume[] getVolumeList();
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index e0e2c35..dcd2103 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6942,14 +6942,22 @@
     field public static final java.lang.String EXTRA_LOG_EVENT_ID = "android.app.backup.extra.LOG_EVENT_ID";
     field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_NAME = "android.app.backup.extra.LOG_EVENT_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_VERSION = "android.app.backup.extra.LOG_EVENT_PACKAGE_VERSION";
+    field public static final java.lang.String EXTRA_LOG_OLD_VERSION = "android.app.backup.extra.LOG_OLD_VERSION";
     field public static final int LOG_EVENT_CATEGORY_AGENT = 2; // 0x2
     field public static final int LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY = 3; // 0x3
     field public static final int LOG_EVENT_CATEGORY_TRANSPORT = 1; // 0x1
+    field public static final int LOG_EVENT_ID_APP_HAS_NO_AGENT = 28; // 0x1c
+    field public static final int LOG_EVENT_ID_CANT_FIND_AGENT = 30; // 0x1e
     field public static final int LOG_EVENT_ID_FULL_BACKUP_TIMEOUT = 4; // 0x4
     field public static final int LOG_EVENT_ID_FULL_RESTORE_TIMEOUT = 45; // 0x2d
     field public static final int LOG_EVENT_ID_KEY_VALUE_BACKUP_TIMEOUT = 21; // 0x15
     field public static final int LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT = 31; // 0x1f
     field public static final int LOG_EVENT_ID_NO_PACKAGES = 49; // 0x31
+    field public static final int LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE = 22; // 0x16
+    field public static final int LOG_EVENT_ID_PACKAGE_NOT_FOUND = 12; // 0xc
+    field public static final int LOG_EVENT_ID_PACKAGE_NOT_PRESENT = 26; // 0x1a
+    field public static final int LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT = 15; // 0xf
+    field public static final int LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER = 36; // 0x24
   }
 
   public abstract class BackupObserver {
@@ -7180,6 +7188,35 @@
 
 package android.app.usage {
 
+  public final class CacheQuotaHint implements android.os.Parcelable {
+    ctor public CacheQuotaHint(android.app.usage.CacheQuotaHint.Builder);
+    method public int describeContents();
+    method public long getQuota();
+    method public int getUid();
+    method public android.app.usage.UsageStats getUsageStats();
+    method public java.lang.String getVolumeUuid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.usage.CacheQuotaHint> CREATOR;
+    field public static final long QUOTA_NOT_SET = -1L; // 0xffffffffffffffffL
+  }
+
+  public static final class CacheQuotaHint.Builder {
+    ctor public CacheQuotaHint.Builder();
+    ctor public CacheQuotaHint.Builder(android.app.usage.CacheQuotaHint);
+    method public android.app.usage.CacheQuotaHint build();
+    method public android.app.usage.CacheQuotaHint.Builder setQuota(long);
+    method public android.app.usage.CacheQuotaHint.Builder setUid(int);
+    method public android.app.usage.CacheQuotaHint.Builder setUsageStats(android.app.usage.UsageStats);
+    method public android.app.usage.CacheQuotaHint.Builder setVolumeUuid(java.lang.String);
+  }
+
+  public abstract class CacheQuotaService extends android.app.Service {
+    ctor public CacheQuotaService();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract java.util.List<android.app.usage.CacheQuotaHint> onComputeCacheQuotaHints(java.util.List<android.app.usage.CacheQuotaHint>);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.app.usage.CacheQuotaService";
+  }
+
   public final class ConfigurationStats implements android.os.Parcelable {
     ctor public ConfigurationStats(android.app.usage.ConfigurationStats);
     method public int describeContents();
@@ -34139,10 +34176,8 @@
     method public void allocateBytes(java.io.File, long, int) throws java.io.IOException;
     method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException;
     method public long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
-    method public long getCacheQuotaBytes();
-    method public long getCacheSizeBytes();
-    method public long getExternalCacheQuotaBytes();
-    method public long getExternalCacheSizeBytes();
+    method public long getCacheQuotaBytes(java.io.File);
+    method public long getCacheSizeBytes(java.io.File);
     method public java.lang.String getMountedObbPath(java.lang.String);
     method public android.os.storage.StorageVolume getPrimaryStorageVolume();
     method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
@@ -49144,6 +49179,7 @@
     method public abstract void setHint(java.lang.CharSequence);
     method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String);
     method public abstract void setLongClickable(boolean);
+    method public abstract void setSanitized(boolean);
     method public abstract void setSelected(boolean);
     method public abstract void setText(java.lang.CharSequence);
     method public abstract void setText(java.lang.CharSequence, int, int);
@@ -49152,7 +49188,6 @@
     method public abstract void setTransformation(android.graphics.Matrix);
     method public abstract void setUrl(java.lang.String);
     method public abstract void setVisibility(int);
-    field public static final int AUTO_FILL_FLAG_SANITIZED = 1; // 0x1
   }
 
   public final class ViewStub extends android.view.View {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 1ba26f5..6773112 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -174,6 +174,10 @@
 package android.os.storage {
 
   public class StorageManager {
+    method public deprecated long getCacheQuotaBytes();
+    method public deprecated long getCacheSizeBytes();
+    method public deprecated long getExternalCacheQuotaBytes();
+    method public deprecated long getExternalCacheSizeBytes();
     method public android.os.storage.StorageVolume getPrimaryVolume();
     method public android.os.storage.StorageVolume[] getVolumeList();
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 5e08f1a..319d9f3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -31374,10 +31374,8 @@
     method public void allocateBytes(java.io.File, long, int) throws java.io.IOException;
     method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException;
     method public long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
-    method public long getCacheQuotaBytes();
-    method public long getCacheSizeBytes();
-    method public long getExternalCacheQuotaBytes();
-    method public long getExternalCacheSizeBytes();
+    method public long getCacheQuotaBytes(java.io.File);
+    method public long getCacheSizeBytes(java.io.File);
     method public java.lang.String getMountedObbPath(java.lang.String);
     method public android.os.storage.StorageVolume getPrimaryStorageVolume();
     method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
@@ -46001,6 +45999,7 @@
     method public abstract void setHint(java.lang.CharSequence);
     method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String);
     method public abstract void setLongClickable(boolean);
+    method public abstract void setSanitized(boolean);
     method public abstract void setSelected(boolean);
     method public abstract void setText(java.lang.CharSequence);
     method public abstract void setText(java.lang.CharSequence, int, int);
@@ -46009,7 +46008,6 @@
     method public abstract void setTransformation(android.graphics.Matrix);
     method public abstract void setUrl(java.lang.String);
     method public abstract void setVisibility(int);
-    field public static final int AUTO_FILL_FLAG_SANITIZED = 1; // 0x1
   }
 
   public final class ViewStub extends android.view.View {
diff --git a/api/test-removed.txt b/api/test-removed.txt
index ab22b6e..e467811 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -180,6 +180,10 @@
 package android.os.storage {
 
   public class StorageManager {
+    method public deprecated long getCacheQuotaBytes();
+    method public deprecated long getCacheSizeBytes();
+    method public deprecated long getExternalCacheQuotaBytes();
+    method public deprecated long getExternalCacheSizeBytes();
     method public android.os.storage.StorageVolume getPrimaryVolume();
     method public android.os.storage.StorageVolume[] getVolumeList();
   }
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 7965fc3..1aef363 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -65,6 +65,7 @@
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.content.PackageHelper;
 import com.android.internal.util.ArrayUtils;
@@ -402,6 +403,7 @@
      * The use of "adb install" or "cmd package install" over "pm install" is highly encouraged.
      */
     private int runInstall() throws RemoteException {
+        long startedTime = SystemClock.elapsedRealtime();
         final InstallParams params = makeInstallParams();
         final String inPath = nextArg();
         if (params.sessionParams.sizeBytes == -1 && !STDIN_PATH.equals(inPath)) {
@@ -435,10 +437,12 @@
                     false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                 return 1;
             }
-            if (doCommitSession(sessionId, false /*logSuccess*/)
-                    != PackageInstaller.STATUS_SUCCESS) {
+            Pair<String, Integer> status = doCommitSession(sessionId, false /*logSuccess*/);
+            if (status.second != PackageInstaller.STATUS_SUCCESS) {
                 return 1;
             }
+            Log.i(TAG, "Package " + status.first + " installed in " + (SystemClock.elapsedRealtime()
+                    - startedTime) + " ms");
             System.out.println("Success");
             return 0;
         } finally {
@@ -456,7 +460,7 @@
 
     private int runInstallCommit() throws RemoteException {
         final int sessionId = Integer.parseInt(nextArg());
-        return doCommitSession(sessionId, true /*logSuccess*/);
+        return doCommitSession(sessionId, true /*logSuccess*/).second;
     }
 
     private int runInstallCreate() throws RemoteException {
@@ -650,7 +654,8 @@
         }
     }
 
-    private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
+    private Pair<String, Integer> doCommitSession(int sessionId, boolean logSuccess)
+            throws RemoteException {
         PackageInstaller.Session session = null;
         try {
             session = new PackageInstaller.Session(
@@ -670,7 +675,7 @@
                 System.err.println("Failure ["
                         + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
             }
-            return status;
+            return new Pair<>(result.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME), status);
         } finally {
             IoUtils.closeQuietly(session);
         }
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 8d385db..13d0f03 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -419,7 +419,7 @@
             mDisplayId = root.getDisplayId();
             mRoot = new ViewNode();
 
-            ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false, 0);
+            ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false);
             if ((root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
                 if (forAutoFill) {
                     // NOTE: flags are currently not supported, hence 0
@@ -1187,11 +1187,10 @@
         final ViewNode mNode;
         final boolean mAsync;
 
-        ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async, int flags) {
+        ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) {
             mAssist = assist;
             mNode = node;
             mAsync = async;
-            mNode.mSanitized = (flags & AUTO_FILL_FLAG_SANITIZED) != 0;
         }
 
         @Override
@@ -1429,16 +1428,15 @@
             ViewNode node = new ViewNode();
             setAutoFillId(node, forAutoFill, virtualId);
             mNode.mChildren[index] = node;
-            return new ViewNodeBuilder(mAssist, node, false, flags);
+            return new ViewNodeBuilder(mAssist, node, false);
         }
 
-        private ViewStructure asyncNewChild(int index, boolean forAutoFill, int virtualId,
-                int flags) {
+        private ViewStructure asyncNewChild(int index, boolean forAutoFill, int virtualId) {
             synchronized (mAssist) {
                 ViewNode node = new ViewNode();
                 setAutoFillId(node, forAutoFill, virtualId);
                 mNode.mChildren[index] = node;
-                ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true, flags);
+                ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true);
                 mAssist.mPendingAsyncChildren.add(builder);
                 return builder;
             }
@@ -1457,12 +1455,12 @@
 
         @Override
         public ViewStructure asyncNewChild(int index) {
-            return asyncNewChild(index, false, 0, 0);
+            return asyncNewChild(index, false, 0);
         }
 
         @Override
         public ViewStructure asyncNewChild(int index, int virtualId, int flags) {
-            return asyncNewChild(index, true, virtualId, flags);
+            return asyncNewChild(index, true, virtualId);
         }
 
         @Override
diff --git a/core/java/android/app/backup/BackupManagerMonitor.java b/core/java/android/app/backup/BackupManagerMonitor.java
index 099878b..d2a623e 100644
--- a/core/java/android/app/backup/BackupManagerMonitor.java
+++ b/core/java/android/app/backup/BackupManagerMonitor.java
@@ -54,15 +54,31 @@
   public static final String EXTRA_LOG_EVENT_CATEGORY =
           "android.app.backup.extra.LOG_EVENT_CATEGORY";
 
+  /**
+   * string: when we have event of id LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER we send the version
+   * of the backup.
+   */
+  public static final String EXTRA_LOG_OLD_VERSION =
+          "android.app.backup.extra.LOG_OLD_VERSION";
+
   // TODO complete this list with all log messages. And document properly.
   public static final int LOG_EVENT_ID_FULL_BACKUP_TIMEOUT = 4;
+  public static final int LOG_EVENT_ID_PACKAGE_NOT_FOUND = 12;
+  public static final int LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT = 15;
   public static final int LOG_EVENT_ID_KEY_VALUE_BACKUP_TIMEOUT = 21;
+  public static final int LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE = 22;
+  public static final int LOG_EVENT_ID_PACKAGE_NOT_PRESENT = 26;
+  public static final int LOG_EVENT_ID_APP_HAS_NO_AGENT = 28;
+  public static final int LOG_EVENT_ID_CANT_FIND_AGENT = 30;
   public static final int LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT = 31;
+  public static final int LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER = 36;
   public static final int LOG_EVENT_ID_FULL_RESTORE_TIMEOUT = 45;
   public static final int LOG_EVENT_ID_NO_PACKAGES = 49;
 
 
 
+
+
   /**
    * This method will be called each time something important happens on BackupManager.
    *
diff --git a/core/java/android/app/usage/CacheQuotaHint.aidl b/core/java/android/app/usage/CacheQuotaHint.aidl
new file mode 100644
index 0000000..0470ea7
--- /dev/null
+++ b/core/java/android/app/usage/CacheQuotaHint.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+/** {@hide} */
+parcelable CacheQuotaHint;
\ No newline at end of file
diff --git a/core/java/android/app/usage/CacheQuotaHint.java b/core/java/android/app/usage/CacheQuotaHint.java
new file mode 100644
index 0000000..4b6f99b
--- /dev/null
+++ b/core/java/android/app/usage/CacheQuotaHint.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * CacheQuotaRequest represents a triplet of a uid, the volume UUID it is stored upon, and
+ * its usage stats. When processed, it obtains a cache quota as defined by the system which
+ * allows apps to understand how much cache to use.
+ * {@hide}
+ */
+@SystemApi
+public final class CacheQuotaHint implements Parcelable {
+    public static final long QUOTA_NOT_SET = -1;
+    private final String mUuid;
+    private final int mUid;
+    private final UsageStats mUsageStats;
+    private final long mQuota;
+
+    /**
+     * Create a new request.
+     * @param builder A builder for this object.
+     */
+    public CacheQuotaHint(Builder builder) {
+        this.mUuid = builder.mUuid;
+        this.mUid = builder.mUid;
+        this.mUsageStats = builder.mUsageStats;
+        this.mQuota = builder.mQuota;
+    }
+
+    public String getVolumeUuid() {
+        return mUuid;
+    }
+
+    public int getUid() {
+        return mUid;
+    }
+
+    public long getQuota() {
+        return mQuota;
+    }
+
+    public UsageStats getUsageStats() {
+        return mUsageStats;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mUuid);
+        dest.writeInt(mUid);
+        dest.writeLong(mQuota);
+        dest.writeParcelable(mUsageStats, 0);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final class Builder {
+        private String mUuid;
+        private int mUid;
+        private UsageStats mUsageStats;
+        private long mQuota;
+
+        public Builder() {
+        }
+
+        public Builder(CacheQuotaHint hint) {
+            setVolumeUuid(hint.getVolumeUuid());
+            setUid(hint.getUid());
+            setUsageStats(hint.getUsageStats());
+            setQuota(hint.getQuota());
+        }
+
+        public @NonNull Builder setVolumeUuid(@Nullable String uuid) {
+            mUuid = uuid;
+            return this;
+        }
+
+        public @NonNull Builder setUid(int uid) {
+            Preconditions.checkArgumentPositive(uid, "Proposed uid was not positive.");
+            mUid = uid;
+            return this;
+        }
+
+        public @NonNull Builder setUsageStats(@Nullable UsageStats stats) {
+            mUsageStats = stats;
+            return this;
+        }
+
+        public @NonNull Builder setQuota(long quota) {
+            Preconditions.checkArgument((quota >= QUOTA_NOT_SET));
+            mQuota = quota;
+            return this;
+        }
+
+        public @NonNull CacheQuotaHint build() {
+            Preconditions.checkNotNull(mUsageStats);
+            return new CacheQuotaHint(this);
+        }
+    }
+
+    public static final Parcelable.Creator<CacheQuotaHint> CREATOR =
+            new Creator<CacheQuotaHint>() {
+                @Override
+                public CacheQuotaHint createFromParcel(Parcel in) {
+                    final Builder builder = new Builder();
+                    return builder.setVolumeUuid(in.readString())
+                            .setUid(in.readInt())
+                            .setQuota(in.readLong())
+                            .setUsageStats(in.readParcelable(UsageStats.class.getClassLoader()))
+                            .build();
+                }
+
+                @Override
+                public CacheQuotaHint[] newArray(int size) {
+                    return new CacheQuotaHint[size];
+                }
+            };
+}
diff --git a/core/java/android/app/usage/CacheQuotaService.java b/core/java/android/app/usage/CacheQuotaService.java
new file mode 100644
index 0000000..b9430ab
--- /dev/null
+++ b/core/java/android/app/usage/CacheQuotaService.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.usage.ICacheQuotaService;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallback;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.List;
+
+/**
+ * CacheQuoteService defines a service which accepts cache quota requests and processes them,
+ * thereby filling out how much quota each request deserves.
+ * {@hide}
+ */
+@SystemApi
+public abstract class CacheQuotaService extends Service {
+    private static final String TAG = "CacheQuotaService";
+
+    /**
+     * The {@link Intent} action that must be declared as handled by a service
+     * in its manifest for the system to recognize it as a quota providing service.
+     */
+    public static final String SERVICE_INTERFACE = "android.app.usage.CacheQuotaService";
+
+    /** {@hide} **/
+    public static final String REQUEST_LIST_KEY = "requests";
+
+    private CacheQuotaServiceWrapper mWrapper;
+    private Handler mHandler;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mWrapper = new CacheQuotaServiceWrapper();
+        mHandler = new ServiceHandler(getMainLooper());
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mWrapper;
+    }
+
+    /**
+     * Processes the cache quota list upon receiving a list of requests.
+     * @param requests A list of cache quotas to fulfill.
+     * @return A completed list of cache quota requests.
+     */
+    public abstract List<CacheQuotaHint> onComputeCacheQuotaHints(
+            List<CacheQuotaHint> requests);
+
+    private final class CacheQuotaServiceWrapper extends ICacheQuotaService.Stub {
+        @Override
+        public void computeCacheQuotaHints(
+                RemoteCallback callback, List<CacheQuotaHint> requests) {
+            final Pair<RemoteCallback, List<CacheQuotaHint>> pair =
+                    Pair.create(callback, requests);
+            Message msg = mHandler.obtainMessage(ServiceHandler.MSG_SEND_LIST, pair);
+            mHandler.sendMessage(msg);
+        }
+    }
+
+    private final class ServiceHandler extends Handler {
+        public static final int MSG_SEND_LIST = 1;
+
+        public ServiceHandler(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            final int action = msg.what;
+            switch (action) {
+                case MSG_SEND_LIST:
+                    final Pair<RemoteCallback, List<CacheQuotaHint>> pair =
+                            (Pair<RemoteCallback, List<CacheQuotaHint>>) msg.obj;
+                    List<CacheQuotaHint> processed = onComputeCacheQuotaHints(pair.second);
+                    final Bundle data = new Bundle();
+                    data.putParcelableList(REQUEST_LIST_KEY, processed);
+
+                    final RemoteCallback callback = pair.first;
+                    callback.sendResult(data);
+                    break;
+                default:
+                    Log.w(TAG, "Handling unknown message: " + action);
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/usage/ICacheQuotaService.aidl b/core/java/android/app/usage/ICacheQuotaService.aidl
new file mode 100644
index 0000000..8d984e0
--- /dev/null
+++ b/core/java/android/app/usage/ICacheQuotaService.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import android.os.RemoteCallback;
+import android.app.usage.CacheQuotaHint;
+
+/** {@hide} */
+oneway interface ICacheQuotaService {
+    void computeCacheQuotaHints(in RemoteCallback callback, in List<CacheQuotaHint> requests);
+}
diff --git a/core/java/android/app/usage/IStorageStatsManager.aidl b/core/java/android/app/usage/IStorageStatsManager.aidl
index 62ebf60..f4c18dd 100644
--- a/core/java/android/app/usage/IStorageStatsManager.aidl
+++ b/core/java/android/app/usage/IStorageStatsManager.aidl
@@ -21,6 +21,7 @@
 
 /** {@hide} */
 interface IStorageStatsManager {
+    boolean isQuotaSupported(String volumeUuid, String callingPackage);
     long getTotalBytes(String volumeUuid, String callingPackage);
     long getFreeBytes(String volumeUuid, String callingPackage);
     StorageStats queryStatsForUid(String volumeUuid, int uid, String callingPackage);
diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java
index 9d30771..7d4efb9 100644
--- a/core/java/android/app/usage/StorageStatsManager.java
+++ b/core/java/android/app/usage/StorageStatsManager.java
@@ -46,6 +46,15 @@
         mService = Preconditions.checkNotNull(service);
     }
 
+    /** {@hide} */
+    public boolean isQuotaSupported(String volumeUuid) {
+        try {
+            return mService.isQuotaSupported(volumeUuid, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Return the total space on the requested storage volume.
      * <p>
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index a6f91fe..08595dd 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -19,7 +19,7 @@
 import android.content.ComponentName;
 import android.content.res.Configuration;
 
-import java.io.IOException;
+import java.util.List;
 
 /**
  * UsageStatsManager local system service interface.
@@ -127,4 +127,7 @@
 
     public abstract void applyRestoredPayload(int user, String key, byte[] payload);
 
+    /* Cache Quota Service API */
+    public abstract List<UsageStats> queryUsageStatsForUser(
+            int userId, int interval, long beginTime, long endTime);
 }
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 7c015de..280fa2c 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -432,14 +432,13 @@
      */
     public static boolean contains(File dir, File file) {
         if (dir == null || file == null) return false;
+        return contains(dir.getAbsolutePath(), file.getAbsolutePath());
+    }
 
-        String dirPath = dir.getAbsolutePath();
-        String filePath = file.getAbsolutePath();
-
+    public static boolean contains(String dirPath, String filePath) {
         if (dirPath.equals(filePath)) {
             return true;
         }
-
         if (!dirPath.endsWith("/")) {
             dirPath += "/";
         }
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 7702c17..8882672 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -546,6 +546,25 @@
     }
 
     /**
+     * Return the filesystem path of the real file on disk that is represented
+     * by the given {@link FileDescriptor}.
+     *
+     * @hide
+     */
+    public static File getFile(FileDescriptor fd) throws IOException {
+        try {
+            final String path = Os.readlink("/proc/self/fd/" + fd.getInt$());
+            if (OsConstants.S_ISREG(Os.stat(path).st_mode)) {
+                return new File(path);
+            } else {
+                throw new IOException("Not a regular file: " + path);
+            }
+        } catch (ErrnoException e) {
+            throw e.rethrowAsIOException();
+        }
+    }
+
+    /**
      * Retrieve the actual FileDescriptor associated with this object.
      *
      * @return Returns the FileDescriptor associated with this object.
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 9e35bf6..2cc4b71 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -293,6 +293,6 @@
     ParcelFileDescriptor openProxyFileDescriptor(int mountPointId, int fileId, int mode) = 74;
     long getCacheQuotaBytes(String volumeUuid, int uid) = 75;
     long getCacheSizeBytes(String volumeUuid, int uid) = 76;
-    long getAllocatableBytes(String path, int flags) = 77;
-    void allocateBytes(String path, long bytes, int flags) = 78;
+    long getAllocatableBytes(String volumeUuid, int flags) = 77;
+    void allocateBytes(String volumeUuid, long bytes, int flags) = 78;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 2a3c03d..e070c6e 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -16,6 +16,7 @@
 
 package android.os.storage;
 
+import static android.net.TrafficStats.GB_IN_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
 
 import android.annotation.IntDef;
@@ -675,6 +676,36 @@
     }
 
     /** {@hide} */
+    public @Nullable String findUuidForPath(File path) {
+        Preconditions.checkNotNull(path);
+        final String pathString = path.getAbsolutePath();
+        if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) {
+            return StorageManager.UUID_PRIVATE_INTERNAL;
+        }
+        try {
+            for (VolumeInfo vol : mStorageManager.getVolumes(0)) {
+                if (vol.path != null && FileUtils.contains(vol.path, pathString)) {
+                    // TODO: verify that emulated adopted devices have UUID of
+                    // underlying volume
+                    return vol.fsUuid;
+                }
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        throw new IllegalStateException("Failed to find a storage device for " + path);
+    }
+
+    /** {@hide} */
+    public @Nullable File findPathForUuid(String volumeUuid) {
+        final VolumeInfo vol = findVolumeByQualifiedUuid(volumeUuid);
+        if (vol != null) {
+            return vol.getPath();
+        }
+        throw new IllegalStateException("Failed to find a storage device for " + volumeUuid);
+    }
+
+    /** {@hide} */
     public @NonNull List<VolumeInfo> getVolumes() {
         try {
             return Arrays.asList(mStorageManager.getVolumes(0));
@@ -1069,9 +1100,12 @@
         throw new IllegalStateException("Missing primary storage");
     }
 
-    /** {@hide} */
-    private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
+    private static final int DEFAULT_THRESHOLD_PERCENTAGE = 5;
     private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
+
+    private static final int DEFAULT_CACHE_PERCENTAGE = 10;
+    private static final long DEFAULT_CACHE_MAX_BYTES = 5 * GB_IN_BYTES;
+
     private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
 
     /**
@@ -1102,6 +1136,23 @@
     }
 
     /**
+     * Return the minimum number of bytes of storage on the device that should
+     * be reserved for cached data.
+     *
+     * @hide
+     */
+    public long getStorageCacheBytes(File path) {
+        final long cachePercent = Settings.Global.getInt(mResolver,
+                Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE, DEFAULT_CACHE_PERCENTAGE);
+        final long cacheBytes = (path.getTotalSpace() * cachePercent) / 100;
+
+        final long maxCacheBytes = Settings.Global.getLong(mResolver,
+                Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES, DEFAULT_CACHE_MAX_BYTES);
+
+        return Math.min(cacheBytes, maxCacheBytes);
+    }
+
+    /**
      * Return the number of available bytes at which the given path is
      * considered full.
      *
@@ -1409,40 +1460,37 @@
     }
 
     /**
-     * Return quota size in bytes for cached data belonging to the calling app.
+     * Return quota size in bytes for all cached data belonging to the calling
+     * app on the filesystem that hosts the given path.
      * <p>
      * If your app goes above this quota, your cached files will be some of the
      * first to be deleted when additional disk space is needed. Conversely, if
      * your app stays under this quota, your cached files will be some of the
      * last to be deleted when additional disk space is needed.
      * <p>
-     * This quota may change over time depending on how frequently the user
+     * This quota will change over time depending on how frequently the user
      * interacts with your app, and depending on how much disk space is used.
-     * <p>
-     * Cached data tracked by this method always includes
-     * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and
-     * it also includes {@link Context#getExternalCacheDir()} if the primary
-     * shared/external storage is hosted on the same storage device as your
-     * private data.
      * <p class="note">
      * Note: if your app uses the {@code android:sharedUserId} manifest feature,
      * then cached data for all packages in your shared UID is tracked together
      * as a single unit.
      * </p>
      *
-     * @see #getCacheSizeBytes()
+     * @see #getCacheSizeBytes(File)
      */
-    public long getCacheQuotaBytes() {
+    public long getCacheQuotaBytes(File path) {
         try {
+            final String volumeUuid = findUuidForPath(path);
             final ApplicationInfo app = mContext.getApplicationInfo();
-            return mStorageManager.getCacheQuotaBytes(app.volumeUuid, app.uid);
+            return mStorageManager.getCacheQuotaBytes(volumeUuid, app.uid);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Return total size in bytes of cached data belonging to the calling app.
+     * Return total size in bytes of all cached data belonging to the calling
+     * app on the filesystem that hosts the given path.
      * <p>
      * Cached data tracked by this method always includes
      * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and
@@ -1457,67 +1505,38 @@
      *
      * @see #getCacheQuotaBytes()
      */
-    public long getCacheSizeBytes() {
+    public long getCacheSizeBytes(File path) {
         try {
+            final String volumeUuid = findUuidForPath(path);
             final ApplicationInfo app = mContext.getApplicationInfo();
-            return mStorageManager.getCacheSizeBytes(app.volumeUuid, app.uid);
+            return mStorageManager.getCacheSizeBytes(volumeUuid, app.uid);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
-    /**
-     * Return quota size in bytes for cached data on primary shared/external
-     * storage belonging to the calling app.
-     * <p>
-     * If primary shared/external storage is hosted on the same storage device
-     * as your private data, this method will return -1, since all data stored
-     * under {@link Context#getExternalCacheDir()} will be counted under
-     * {@link #getCacheQuotaBytes()}.
-     * <p class="note">
-     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
-     * then cached data for all packages in your shared UID is tracked together
-     * as a single unit.
-     * </p>
-     */
+    /** @removed */
+    @Deprecated
+    public long getCacheQuotaBytes() {
+        return getCacheQuotaBytes(mContext.getCacheDir());
+    }
+
+    /** @removed */
+    @Deprecated
+    public long getCacheSizeBytes() {
+        return getCacheSizeBytes(mContext.getCacheDir());
+    }
+
+    /** @removed */
+    @Deprecated
     public long getExternalCacheQuotaBytes() {
-        final ApplicationInfo app = mContext.getApplicationInfo();
-        final String primaryUuid = getPrimaryStorageUuid();
-        if (Objects.equals(app.volumeUuid, primaryUuid)) {
-            return -1;
-        }
-        try {
-            return mStorageManager.getCacheQuotaBytes(primaryUuid, app.uid);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getCacheQuotaBytes(mContext.getExternalCacheDir());
     }
 
-    /**
-     * Return total size in bytes of cached data on primary shared/external
-     * storage belonging to the calling app.
-     * <p>
-     * If primary shared/external storage is hosted on the same storage device
-     * as your private data, this method will return -1, since all data stored
-     * under {@link Context#getExternalCacheDir()} will be counted under
-     * {@link #getCacheQuotaBytes()}.
-     * <p class="note">
-     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
-     * then cached data for all packages in your shared UID is tracked together
-     * as a single unit.
-     * </p>
-     */
+    /** @removed */
+    @Deprecated
     public long getExternalCacheSizeBytes() {
-        final ApplicationInfo app = mContext.getApplicationInfo();
-        final String primaryUuid = getPrimaryStorageUuid();
-        if (Objects.equals(app.volumeUuid, primaryUuid)) {
-            return -1;
-        }
-        try {
-            return mStorageManager.getCacheSizeBytes(primaryUuid, app.uid);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getCacheSizeBytes(mContext.getExternalCacheDir());
     }
 
     /**
@@ -1551,28 +1570,30 @@
      * Return the maximum number of new bytes that your app can allocate for
      * itself using {@link #allocateBytes(File, long, int)} at the given path.
      * This value is typically larger than {@link File#getUsableSpace()}, since
-     * the system may automatically delete cached files to satisfy your request.
+     * the system may be willing to delete cached files to satisfy an allocation
+     * request.
      * <p>
      * This method is best used as a pre-flight check, such as deciding if there
      * is enough space to store an entire music album before you allocate space
      * for each audio file in the album. Attempts to allocate disk space beyond
-     * this value will fail.
+     * the returned value will fail.
      * <p class="note">
      * Note: if your app uses the {@code android:sharedUserId} manifest feature,
      * then allocatable space for all packages in your shared UID is tracked
      * together as a single unit.
      * </p>
      *
-     * @param file the directory where you're considering allocating disk space,
+     * @param path the path where you're considering allocating disk space,
      *            since allocatable space can vary widely depending on the
      *            underlying storage device.
      * @param flags to apply to the request.
      * @return the maximum number of new bytes that the calling app can allocate
      *         using {@link #allocateBytes(File, long, int)}.
      */
-    public long getAllocatableBytes(File file, @AllocateFlags int flags) throws IOException {
+    public long getAllocatableBytes(File path, @AllocateFlags int flags) throws IOException {
         try {
-            return mStorageManager.getAllocatableBytes(file.getAbsolutePath(), flags);
+            final String volumeUuid = findUuidForPath(path);
+            return mStorageManager.getAllocatableBytes(volumeUuid, flags);
         } catch (ParcelableException e) {
             e.maybeRethrow(IOException.class);
             throw new RuntimeException(e);
@@ -1594,14 +1615,15 @@
      * {@link #allocateBytes(FileDescriptor, long, int)} which will guarantee
      * that bytes are allocated to an opened file.
      *
-     * @param file the directory where you'd like to allocate disk space.
+     * @param path the path where you'd like to allocate disk space.
      * @param bytes the number of bytes to allocate.
      * @param flags to apply to the request.
      * @see #getAllocatableBytes(File, int)
      */
-    public void allocateBytes(File file, long bytes, @AllocateFlags int flags) throws IOException {
+    public void allocateBytes(File path, long bytes, @AllocateFlags int flags) throws IOException {
         try {
-            mStorageManager.allocateBytes(file.getAbsolutePath(), bytes, flags);
+            final String volumeUuid = findUuidForPath(path);
+            mStorageManager.allocateBytes(volumeUuid, bytes, flags);
         } catch (ParcelableException e) {
             e.maybeRethrow(IOException.class);
         } catch (RemoteException e) {
@@ -1610,37 +1632,39 @@
     }
 
     /**
-     * Allocate the requested number of bytes for your application to use at the
-     * given path. This will cause the system to delete any cached files
+     * Allocate the requested number of bytes for your application to use in the
+     * given open file. This will cause the system to delete any cached files
      * necessary to satisfy your request.
      * <p>
      * Attempts to allocate disk space beyond the value returned by
      * {@link #getAllocatableBytes(File, int)} will fail.
      * <p>
-     * This method guarantees that bytes are allocated to the opened file,
-     * otherwise it will throw if fast allocation not possible. Fast allocation
-     * is typically only supported in private app data directories, and on
-     * shared/external storage devices which are emulated.
+     * This method guarantees that bytes have been allocated to the opened file,
+     * otherwise it will throw if fast allocation is not possible. Fast
+     * allocation is typically only supported in private app data directories,
+     * and on shared/external storage devices which are emulated.
      *
-     * @param fd the directory where you'd like to allocate disk space.
-     * @param bytes the number of bytes to allocate.
+     * @param fd the open file that you'd like to allocate disk space for.
+     * @param bytes the number of bytes to allocate. This is the desired final
+     *            size of the open file.
      * @param flags to apply to the request.
      * @see #getAllocatableBytes(File, int)
      * @see Environment#isExternalStorageEmulated(File)
      */
     public void allocateBytes(FileDescriptor fd, long bytes, @AllocateFlags int flags)
             throws IOException {
-        final File file;
-        try {
-            file = new File(Os.readlink("/proc/self/fd/" + fd.getInt$()));
-        } catch (ErrnoException e) {
-            throw e.rethrowAsIOException();
-        }
+        final File file = ParcelFileDescriptor.getFile(fd);
         for (int i = 0; i < 3; i++) {
-            allocateBytes(file, bytes, flags);
-
             try {
+                final long haveBytes = Os.fstat(fd).st_blocks * 512;
+                final long needBytes = bytes - haveBytes;
+
+                if (needBytes > 0) {
+                    allocateBytes(file, needBytes, flags);
+                }
+
                 Os.posix_fallocate(fd, 0, bytes);
+                return;
             } catch (ErrnoException e) {
                 if (e.errno == OsConstants.ENOSPC) {
                     Log.w(TAG, "Odd, not enough space; let's try again?");
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c9b1c9c..ebc5b88 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8578,6 +8578,24 @@
                 SYS_STORAGE_FULL_THRESHOLD_BYTES = "sys_storage_full_threshold_bytes";
 
         /**
+         * Minimum percentage of storage on the device that is reserved for
+         * cached data.
+         *
+         * @hide
+         */
+        public static final String
+                SYS_STORAGE_CACHE_PERCENTAGE = "sys_storage_cache_percentage";
+
+        /**
+         * Maximum bytes of storage on the device that is reserved for cached
+         * data.
+         *
+         * @hide
+         */
+        public static final String
+                SYS_STORAGE_CACHE_MAX_BYTES = "sys_storage_cache_max_bytes";
+
+        /**
          * The maximum reconnect delay for short network outages or when the
          * network is suspended due to phone use.
          *
diff --git a/core/java/android/view/IGraphicsStats.aidl b/core/java/android/view/IGraphicsStats.aidl
index c235eb2..e6b750b 100644
--- a/core/java/android/view/IGraphicsStats.aidl
+++ b/core/java/android/view/IGraphicsStats.aidl
@@ -17,10 +17,11 @@
 package android.view;
 
 import android.os.ParcelFileDescriptor;
+import android.view.IGraphicsStatsCallback;
 
 /**
  * @hide
  */
 interface IGraphicsStats {
-    ParcelFileDescriptor requestBufferForProcess(String packageName, IBinder token);
+    ParcelFileDescriptor requestBufferForProcess(String packageName, IGraphicsStatsCallback callback);
 }
diff --git a/core/java/android/view/IGraphicsStatsCallback.aidl b/core/java/android/view/IGraphicsStatsCallback.aidl
new file mode 100644
index 0000000..f70e141
--- /dev/null
+++ b/core/java/android/view/IGraphicsStatsCallback.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+/**
+ * @hide
+ */
+oneway interface IGraphicsStatsCallback {
+    void onRotateGraphicsStatsBuffer();
+}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 4ceb236..c66bf874 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -25,9 +25,9 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.AnimatedVectorDrawable;
-import android.os.Binder;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.Trace;
 import android.util.Log;
@@ -164,6 +164,18 @@
     public static final String OVERDRAW_PROPERTY_SHOW = "show";
 
     /**
+     * Defines the rendering pipeline to be used by the ThreadedRenderer.
+     *
+     * Possible values:
+     * "opengl", will use the existing OpenGL renderer
+     * "skiagl", will use Skia's OpenGL renderer
+     * "skiavk", will use Skia's Vulkan renderer
+     *
+     * @hide
+     */
+    public static final String DEBUG_RENDERER_PROPERTY = "debug.hwui.renderer";
+
+    /**
      * Turn on to debug non-rectangular clip operations.
      *
      * Possible values:
@@ -248,10 +260,10 @@
      *
      * @return A threaded renderer backed by OpenGL.
      */
-    public static ThreadedRenderer create(Context context, boolean translucent) {
+    public static ThreadedRenderer create(Context context, boolean translucent, String name) {
         ThreadedRenderer renderer = null;
         if (isAvailable()) {
-            renderer = new ThreadedRenderer(context, translucent);
+            renderer = new ThreadedRenderer(context, translucent, name);
         }
         return renderer;
     }
@@ -275,10 +287,6 @@
         nOverrideProperty(name, value);
     }
 
-    public static void dumpProfileData(byte[] data, FileDescriptor fd) {
-        nDumpProfileData(data, fd);
-    }
-
     // Keep in sync with DrawFrameTask.h SYNC_* flags
     // Nothing interesting to report
     private static final int SYNC_OK = 0;
@@ -334,7 +342,7 @@
     private boolean mEnabled;
     private boolean mRequested = true;
 
-    ThreadedRenderer(Context context, boolean translucent) {
+    ThreadedRenderer(Context context, boolean translucent, String name) {
         final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
         mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
         mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
@@ -348,6 +356,7 @@
         mRootNode = RenderNode.adopt(rootNodePtr);
         mRootNode.setClipToBounds(false);
         mNativeProxy = nCreateProxy(translucent, rootNodePtr);
+        nSetName(mNativeProxy, name);
 
         ProcessInitializer.sInstance.init(context, mNativeProxy);
 
@@ -815,15 +824,6 @@
     }
 
     /**
-     * Optional, sets the name of the renderer. Useful for debugging purposes.
-     *
-     * @param name The name of this renderer, can be null
-     */
-    void setName(String name) {
-        nSetName(mNativeProxy, name);
-    }
-
-    /**
      * Blocks until all previously queued work has completed.
      */
     void fence() {
@@ -884,20 +884,29 @@
 
     private static class ProcessInitializer {
         static ProcessInitializer sInstance = new ProcessInitializer();
-        private static IBinder sProcToken;
 
         private boolean mInitialized = false;
 
+        private Context mAppContext;
+        private IGraphicsStats mGraphicsStatsService;
+        private IGraphicsStatsCallback mGraphicsStatsCallback = new IGraphicsStatsCallback.Stub() {
+            @Override
+            public void onRotateGraphicsStatsBuffer() throws RemoteException {
+                rotateBuffer();
+            }
+        };
+
         private ProcessInitializer() {}
 
         synchronized void init(Context context, long renderProxy) {
             if (mInitialized) return;
             mInitialized = true;
+            mAppContext = context.getApplicationContext();
             initSched(context, renderProxy);
-            initGraphicsStats(context, renderProxy);
+            initGraphicsStats();
         }
 
-        private static void initSched(Context context, long renderProxy) {
+        private void initSched(Context context, long renderProxy) {
             try {
                 int tid = nGetRenderThreadTid(renderProxy);
                 ActivityManager.getService().setRenderThread(tid);
@@ -906,17 +915,28 @@
             }
         }
 
-        private static void initGraphicsStats(Context context, long renderProxy) {
+        private void initGraphicsStats() {
             try {
                 IBinder binder = ServiceManager.getService("graphicsstats");
                 if (binder == null) return;
-                IGraphicsStats graphicsStatsService = IGraphicsStats.Stub
-                        .asInterface(binder);
-                sProcToken = new Binder();
-                final String pkg = context.getApplicationInfo().packageName;
-                ParcelFileDescriptor pfd = graphicsStatsService.
-                        requestBufferForProcess(pkg, sProcToken);
-                nSetProcessStatsBuffer(renderProxy, pfd.getFd());
+                mGraphicsStatsService = IGraphicsStats.Stub.asInterface(binder);
+                requestBuffer();
+            } catch (Throwable t) {
+                Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t);
+            }
+        }
+
+        private void rotateBuffer() {
+            nRotateProcessStatsBuffer();
+            requestBuffer();
+        }
+
+        private void requestBuffer() {
+            try {
+                final String pkg = mAppContext.getApplicationInfo().packageName;
+                ParcelFileDescriptor pfd = mGraphicsStatsService
+                        .requestBufferForProcess(pkg, mGraphicsStatsCallback);
+                nSetProcessStatsBuffer(pfd.getFd());
                 pfd.close();
             } catch (Throwable t) {
                 Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t);
@@ -936,7 +956,8 @@
 
     static native void setupShadersDiskCache(String cacheFile);
 
-    private static native void nSetProcessStatsBuffer(long nativeProxy, int fd);
+    private static native void nRotateProcessStatsBuffer();
+    private static native void nSetProcessStatsBuffer(int fd);
     private static native int nGetRenderThreadTid(long nativeProxy);
 
     private static native long nCreateRootRenderNode();
@@ -981,7 +1002,6 @@
 
     private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
             @DumpFlags int dumpFlags);
-    private static native void nDumpProfileData(byte[] data, FileDescriptor fd);
 
     private static native void nAddRenderNode(long nativeProxy, long rootRenderNode,
              boolean placeFront);
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 5d01b416..f16fcc9 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -35,7 +35,7 @@
      * Defines the width of the horizontal scrollbar and the height of the vertical scrollbar in
      * dips
      */
-    private static final int SCROLL_BAR_SIZE = 10;
+    private static final int SCROLL_BAR_SIZE = 4;
 
     /**
      * Duration of the fade when scrollbars fade away in milliseconds
@@ -346,7 +346,8 @@
 
         mEdgeSlop = (int) (sizeAndDensity * EDGE_SLOP + 0.5f);
         mFadingEdgeLength = (int) (sizeAndDensity * FADING_EDGE_LENGTH + 0.5f);
-        mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
+        mScrollbarSize = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.config_scrollbarSize);
         mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f);
         mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 6cdd483..c81e938 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -887,9 +887,9 @@
                 final boolean hasSurfaceInsets = insets.left != 0 || insets.right != 0
                         || insets.top != 0 || insets.bottom != 0;
                 final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets;
-                mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent);
+                mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
+                        attrs.getTitle().toString());
                 if (mAttachInfo.mThreadedRenderer != null) {
-                    mAttachInfo.mThreadedRenderer.setName(attrs.getTitle().toString());
                     mAttachInfo.mHardwareAccelerated =
                             mAttachInfo.mHardwareAccelerationRequested = true;
                 }
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 9ce23e6..bc2725f 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -31,16 +31,6 @@
 public abstract class ViewStructure {
 
     /**
-     * Flag used when adding virtual views for auto-fill, it indicates the contents of the view
-     * (such as * {@link android.app.assist.AssistStructure.ViewNode#getText()} and
-     * {@link android.app.assist.AssistStructure.ViewNode#getAutoFillValue()})
-     * can be passed to the {@link
-     * android.service.autofill.AutoFillService#onFillRequest(android.app.assist.AssistStructure,
-     * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)} call.
-     */
-    public static final int AUTO_FILL_FLAG_SANITIZED = 0x1;
-
-    /**
      * Set the identifier for this view.
      *
      * @param id The view's identifier, as per {@link View#getId View.getId()}.
@@ -278,7 +268,7 @@
      *
      * @param index child index
      * @param virtualId id identifying the virtual child inside the custom view.
-     * @param flags currently {@code 0} or {@link #AUTO_FILL_FLAG_SANITIZED}.
+     * @param flags currently {@code 0}.
      */
     // TODO(b/33197203, b/33802548): add CTS/unit test
     public abstract ViewStructure newChild(int index, int virtualId, int flags);
@@ -299,7 +289,7 @@
      *
      * @param index child index
      * @param virtualId id identifying the virtual child inside the custom view.
-     * @param flags currently {@code 0} or {@link #AUTO_FILL_FLAG_SANITIZED}.
+     * @param flags currently {@code 0}.
      */
     // TODO(b/33197203, b/33802548): add CTS/unit test
     public abstract ViewStructure asyncNewChild(int index, int virtualId, int flags);
@@ -317,12 +307,19 @@
     public abstract void setAutoFillValue(AutoFillValue value);
 
     /**
-     * @hide
+     * Marks this node as sanitized so its content are sent on {@link
+     * android.service.autofill.AutoFillService#onFillRequest(android.app.assist.AssistStructure,
+     * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)}.
      *
-     * TODO(b/33197203, b/33269702): temporary set it as not sanitized until
-     * AssistStructure automaticaly sets sanitization based on text coming from resources
+     * <p>Only nodes that does not have PII (Personally Identifiable Information - sensitive data
+     * such as email addresses, credit card numbers, passwords, etc...) should be marked
+     * as sanitized; a good rule of thumb is to mark as sanitized nodes whose value were statically
+     * set from resources.
+     *
+     * <p>Should only be set when the node is used for AutoFill purposes - it will be ignored
+     * when used for Assist.
      */
-    public abstract void setSanitized(boolean sensitive);
+    public abstract void setSanitized(boolean sanitized);
 
     /**
      * Call when done populating a {@link ViewStructure} returned by
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 051ffee..bfaddaf 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -730,6 +730,10 @@
     // Watcher used to notify changes to auto-fill manager.
     private AutoFillChangeWatcher mAutoFillChangeWatcher;
 
+    // Indicates whether the text was set from resources or dynamically, so it can be used to
+    // sanitize auto-fill request.
+    private boolean mTextFromResource = false;
+
     /**
      * Kick-start the font cache for the zygote process (to pay the cost of
      * initializing freetype for our default font only once).
@@ -948,6 +952,8 @@
                     attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);
 
         int n = a.getIndexCount();
+
+        boolean fromResourceId = false;
         for (int i = 0; i < n; i++) {
             int attr = a.getIndex(i);
 
@@ -1089,6 +1095,7 @@
                     break;
 
                 case com.android.internal.R.styleable.TextView_text:
+                    fromResourceId = true;
                     text = a.getText(attr);
                     break;
 
@@ -1567,6 +1574,10 @@
         }
 
         setText(text, bufferType);
+        if (fromResourceId) {
+            mTextFromResource = true;
+        }
+
         if (hint != null) setHint(hint);
 
         /*
@@ -5067,6 +5078,7 @@
 
     private void setText(CharSequence text, BufferType type,
                          boolean notifyBefore, int oldlen) {
+        mTextFromResource = false;
         if (text == null) {
             text = "";
         }
@@ -5301,6 +5313,7 @@
     @android.view.RemotableViewMethod
     public final void setText(@StringRes int resid) {
         setText(getContext().getResources().getText(resid));
+        mTextFromResource = true;
     }
 
     /**
@@ -5327,6 +5340,7 @@
      */
     public final void setText(@StringRes int resid, BufferType type) {
         setText(getContext().getResources().getText(resid), type);
+        mTextFromResource = true;
     }
 
     /**
@@ -9872,9 +9886,7 @@
         final boolean isPassword = hasPasswordTransformationMethod()
                 || isPasswordInputType(getInputType());
         if (forAutoFill) {
-            // TODO(b/33197203, b/33269702): temporary set it as not sanitized until
-            // AssistStructure automaticaly sets sanitization based on text coming from resources
-            structure.setSanitized(!isPassword);
+            structure.setSanitized(mTextFromResource);
             if (mAutoFillChangeWatcher == null && isTextEditable()) {
                 mAutoFillChangeWatcher = new AutoFillChangeWatcher();
                 addTextChangedListener(mAutoFillChangeWatcher);
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 0099673..2b1da25 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -34,6 +34,7 @@
 
 #include "jni.h"
 #include "JNIHelp.h"
+#include "ScopedLocalRef.h"
 
 // ----------------------------------------------------------------------------
 
@@ -385,7 +386,6 @@
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gSurfaceTextureMethods[] = {
-    {"nativeClassInit",            "()V",   (void*)SurfaceTexture_classInit },
     {"nativeInit",                 "(ZIZLjava/lang/ref/WeakReference;)V", (void*)SurfaceTexture_init },
     {"nativeFinalize",             "()V",   (void*)SurfaceTexture_finalize },
     {"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
@@ -401,6 +401,10 @@
 
 int register_android_graphics_SurfaceTexture(JNIEnv* env)
 {
+    // Cache some fields.
+    ScopedLocalRef<jclass> klass(env, FindClassOrDie(env, kSurfaceTextureClassPathName));
+    SurfaceTexture_classInit(env, klass.get());
+
     return RegisterMethodsOrDie(env, kSurfaceTextureClassPathName, gSurfaceTextureMethods,
                                 NELEM(gSurfaceTextureMethods));
 }
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index b57f2362..a03d3c5 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -252,25 +252,26 @@
             if (t_pri >= ANDROID_PRIORITY_BACKGROUND) {
                 // This task wants to stay at background
                 // update its cpuset so it doesn't only run on bg core(s)
-#ifdef ENABLE_CPUSETS
-                int err = set_cpuset_policy(t_pid, sp);
-                if (err != NO_ERROR) {
-                    signalExceptionForGroupError(env, -err, t_pid);
-                    break;
+                if (cpusets_enabled()) {
+                    int err = set_cpuset_policy(t_pid, sp);
+                    if (err != NO_ERROR) {
+                        signalExceptionForGroupError(env, -err, t_pid);
+                        break;
+                    }
                 }
-#endif
                 continue;
             }
         }
         int err;
-#ifdef ENABLE_CPUSETS
-        // set both cpuset and cgroup for general threads
-        err = set_cpuset_policy(t_pid, sp);
-        if (err != NO_ERROR) {
-            signalExceptionForGroupError(env, -err, t_pid);
-            break;
+
+        if (cpusets_enabled()) {
+            // set both cpuset and cgroup for general threads
+            err = set_cpuset_policy(t_pid, sp);
+            if (err != NO_ERROR) {
+                signalExceptionForGroupError(env, -err, t_pid);
+                break;
+            }
         }
-#endif
 
         err = set_sched_policy(t_pid, sp);
         if (err != NO_ERROR) {
@@ -291,7 +292,6 @@
     return (int) sp;
 }
 
-#ifdef ENABLE_CPUSETS
 /** Sample CPUset list format:
  *  0-3,4,6-8
  */
@@ -367,7 +367,6 @@
     }
     return;
 }
-#endif
 
 
 /**
@@ -376,22 +375,21 @@
  * them in the passed in cpu_set_t
  */
 void get_exclusive_cpuset_cores(SchedPolicy policy, cpu_set_t *cpu_set) {
-#ifdef ENABLE_CPUSETS
-    int i;
-    cpu_set_t tmp_set;
-    get_cpuset_cores_for_policy(policy, cpu_set);
-    for (i = 0; i < SP_CNT; i++) {
-        if ((SchedPolicy) i == policy) continue;
-        get_cpuset_cores_for_policy((SchedPolicy)i, &tmp_set);
-        // First get cores exclusive to one set or the other
-        CPU_XOR(&tmp_set, cpu_set, &tmp_set);
-        // Then get the ones only in cpu_set
-        CPU_AND(cpu_set, cpu_set, &tmp_set);
+    if (cpusets_enabled()) {
+        int i;
+        cpu_set_t tmp_set;
+        get_cpuset_cores_for_policy(policy, cpu_set);
+        for (i = 0; i < SP_CNT; i++) {
+            if ((SchedPolicy) i == policy) continue;
+            get_cpuset_cores_for_policy((SchedPolicy)i, &tmp_set);
+            // First get cores exclusive to one set or the other
+            CPU_XOR(&tmp_set, cpu_set, &tmp_set);
+            // Then get the ones only in cpu_set
+            CPU_AND(cpu_set, cpu_set, &tmp_set);
+        }
+    } else {
+        CPU_ZERO(cpu_set);
     }
-#else
-    (void) policy;
-    CPU_ZERO(cpu_set);
-#endif
     return;
 }
 
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index d37f96a..37eae48a 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -43,7 +43,6 @@
 #include <FrameInfo.h>
 #include <FrameMetricsObserver.h>
 #include <IContextFactory.h>
-#include <JankTracker.h>
 #include <PropertyValuesAnimatorSet.h>
 #include <RenderNode.h>
 #include <renderthread/CanvasContext.h>
@@ -587,10 +586,13 @@
     return atoi(prop) > 0 ? JNI_TRUE : JNI_FALSE;
 }
 
+static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) {
+    RenderProxy::rotateProcessStatsBuffer();
+}
+
 static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jint fd) {
-    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->setProcessStatsBuffer(fd);
+        jint fd) {
+    RenderProxy::setProcessStatsBuffer(fd);
 }
 
 static jint android_view_ThreadedRenderer_getRenderThreadTid(JNIEnv* env, jobject clazz,
@@ -817,15 +819,6 @@
     proxy->dumpProfileInfo(fd, dumpFlags);
 }
 
-static void android_view_ThreadedRenderer_dumpProfileData(JNIEnv* env, jobject clazz,
-        jbyteArray jdata, jobject javaFileDescriptor) {
-    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
-    ScopedByteArrayRO buffer(env, jdata);
-    if (buffer.get()) {
-        JankTracker::dumpBuffer(buffer.get(), buffer.size(), fd);
-    }
-}
-
 static void android_view_ThreadedRenderer_addRenderNode(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlong renderNodePtr, jboolean placeFront) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -910,7 +903,8 @@
 
 static const JNINativeMethod gMethods[] = {
     { "nSupportsOpenGL", "()Z", (void*) android_view_ThreadedRenderer_supportsOpenGL },
-    { "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
+    { "nRotateProcessStatsBuffer", "()V", (void*) android_view_ThreadedRenderer_rotateProcessStatsBuffer },
+    { "nSetProcessStatsBuffer", "(I)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
     { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid },
     { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
     { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
@@ -943,7 +937,6 @@
     { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
     { "nSerializeDisplayListTree", "(J)V", (void*) android_view_ThreadedRenderer_serializeDisplayListTree },
     { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
-    { "nDumpProfileData", "([BLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileData },
     { "setupShadersDiskCache", "(Ljava/lang/String;)V",
                 (void*) android_view_ThreadedRenderer_setupShadersDiskCache },
     { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode},
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index ba1d664..a2f07d9 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,6 +21,7 @@
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 import "frameworks/base/core/proto/android/service/appwidget.proto";
+import "frameworks/base/core/proto/android/service/graphicsstats.proto";
 import "frameworks/base/core/proto/android/service/fingerprint.proto";
 import "frameworks/base/core/proto/android/service/netstats.proto";
 import "frameworks/base/core/proto/android/service/notification.proto";
@@ -57,4 +58,5 @@
     android.providers.settings.SettingsServiceDumpProto settings = 3002;
     android.service.appwidget.AppWidgetServiceDumpProto appwidget = 3003;
     android.service.notification.NotificationServiceDumpProto notification = 3004;
+    android.service.GraphicsStatsServiceDumpProto graphicsstats = 3005;
 }
diff --git a/core/proto/android/service/graphicsstats.proto b/core/proto/android/service/graphicsstats.proto
new file mode 100644
index 0000000..6dbfe48
--- /dev/null
+++ b/core/proto/android/service/graphicsstats.proto
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+package android.service;
+
+option java_multiple_files = true;
+option java_outer_classname = "GraphicsStatsServiceProto";
+
+message GraphicsStatsServiceDumpProto {
+    repeated GraphicsStatsProto stats = 1;
+}
+
+message GraphicsStatsProto {
+
+    // The package name of the app
+    string package_name = 1;
+
+    // The version code of the app
+    int32 version_code = 2;
+
+    // The start & end timestamps in UTC as
+    // milliseconds since January 1, 1970
+    // Compatible with java.util.Date#setTime()
+    int64 stats_start = 3;
+    int64 stats_end = 4;
+
+    // The aggregated statistics for the package
+    GraphicsStatsJankSummaryProto summary = 5;
+
+    // The frame time histogram for the package
+    repeated GraphicsStatsHistogramBucketProto histogram = 6;
+}
+
+message GraphicsStatsJankSummaryProto {
+    // Distinct frame count.
+    int32 total_frames = 1;
+
+    // Number of frames with slow render time. Frames are considered janky if
+    // they took more than a vsync interval (typically 16.667ms) to be rendered.
+    int32 janky_frames = 2;
+
+    // Number of "missed vsync" events.
+    int32 missed_vsync_count = 3;
+
+    // Number of "high input latency" events.
+    int32 high_input_latency_count = 4;
+
+    // Number of "slow UI thread" events.
+    int32 slow_ui_thread_count = 5;
+
+    // Number of "slow bitmap upload" events.
+    int32 slow_bitmap_upload_count = 6;
+
+    // Number of "slow draw" events.
+    int32 slow_draw_count = 7;
+}
+
+message GraphicsStatsHistogramBucketProto {
+    // Lower bound of render time in milliseconds.
+    int32 render_millis = 1;
+    // Number of frames in the bucket.
+    int32 frame_count = 2;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ff70a69..9108f4a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3109,6 +3109,13 @@
     <permission android:name="android.permission.BIND_DREAM_SERVICE"
         android:protectionLevel="signature" />
 
+    <!-- Must be required by an {@link android.app.usage.CacheQuotaService} to ensure that only the
+         system can bind to it.
+         @hide This is not a third-party API (intended for OEMs and system apps).
+    -->
+    <permission android:name="android.permission.BIND_CACHE_QUOTA_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to call into a carrier setup flow. It is up to the
          carrier setup application to enforce that this permission is required
          @hide This is not a third-party API (intended for OEMs and system apps). -->
diff --git a/core/res/res/drawable/scrollbar_handle_material.xml b/core/res/res/drawable/scrollbar_handle_material.xml
index 33efbba..f020112 100644
--- a/core/res/res/drawable/scrollbar_handle_material.xml
+++ b/core/res/res/drawable/scrollbar_handle_material.xml
@@ -19,7 +19,4 @@
        android:shape="rectangle">
     <solid
         android:color="#84ffffff" />
-    <size
-        android:width="4dp"
-        android:height="4dp" />
 </shape>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ee7ea6a..d5ffdd0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1873,6 +1873,10 @@
     <!-- Amount of time in ms the user needs to press the relevant key to bring up the global actions dialog -->
     <integer name="config_globalActionsKeyTimeout">500</integer>
 
+    <!-- Default width of a vertical scrollbar and height of a horizontal scrollbar.
+         Takes effect only if the scrollbar drawables have no intrinsic size. -->
+    <dimen name="config_scrollbarSize">4dp</dimen>
+
     <!-- Distance that should be scrolled in response to a {@link MotionEvent#ACTION_SCROLL event}
          with an axis value of 1. -->
     <dimen name="config_scrollFactor">64dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 89ded8d..b5a5125 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -428,6 +428,7 @@
   <java-symbol type="dimen" name="config_viewConfigurationTouchSlop" />
   <java-symbol type="dimen" name="config_viewMinFlingVelocity" />
   <java-symbol type="dimen" name="config_viewMaxFlingVelocity" />
+  <java-symbol type="dimen" name="config_scrollbarSize" />
   <java-symbol type="dimen" name="config_scrollFactor" />
   <java-symbol type="dimen" name="default_app_widget_padding_bottom" />
   <java-symbol type="dimen" name="default_app_widget_padding_left" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index b063baf..400fb47 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -214,7 +214,7 @@
         <!-- Scrollbar attributes -->
         <item name="scrollbarFadeDuration">250</item>
         <item name="scrollbarDefaultDelayBeforeFade">400</item>
-        <item name="scrollbarSize">10dp</item>
+        <item name="scrollbarSize">@dimen/config_scrollbarSize</item>
         <item name="scrollbarThumbHorizontal">@drawable/scrollbar_handle_material</item>
         <item name="scrollbarThumbVertical">@drawable/config_scrollbarThumbVertical</item>
         <item name="scrollbarTrackHorizontal">@null</item>
@@ -583,7 +583,7 @@
         <!-- Scrollbar attributes -->
         <item name="scrollbarFadeDuration">250</item>
         <item name="scrollbarDefaultDelayBeforeFade">400</item>
-        <item name="scrollbarSize">10dp</item>
+        <item name="scrollbarSize">@dimen/config_scrollbarSize</item>
         <item name="scrollbarThumbHorizontal">@drawable/scrollbar_handle_material</item>
         <item name="scrollbarThumbVertical">@drawable/config_scrollbarThumbVertical</item>
         <item name="scrollbarTrackHorizontal">@null</item>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 76331a8..48435bd 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -305,6 +305,8 @@
                     Settings.Global.STORAGE_BENCHMARK_INTERVAL,
                     Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
                     Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
+                    Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES,
+                    Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE,
                     Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
                     Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES,
                     Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE,
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index efb46b90..90bdd81 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -403,11 +403,4 @@
     private native int nativeGetQueuedCount();
     private native void nativeRelease();
     private native boolean nativeIsReleased();
-
-    /*
-     * We use a class initializer to allow the native code to cache some
-     * field offsets.
-     */
-    private static native void nativeClassInit();
-    static { nativeClassInit(); }
 }
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 166863c..5694115 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -98,9 +98,14 @@
             if (dupAshmemFd < 0) {
                 result = -errno;
             } else {
+                // the size of the ashmem descriptor can be modified between ashmem_get_size_region
+                // call and mmap, so we'll check again immediately after memory is mapped
                 void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0);
                 if (data == MAP_FAILED) {
                     result = -errno;
+                } else if (ashmem_get_size_region(dupAshmemFd) != size) {
+                    ::munmap(data, size);
+                    result = BAD_VALUE;
                 } else {
                     CursorWindow* window = new CursorWindow(name, dupAshmemFd,
                             data, size, true /*readOnly*/);
diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h
index f543565..ad64b24 100644
--- a/libs/androidfw/include/androidfw/CursorWindow.h
+++ b/libs/androidfw/include/androidfw/CursorWindow.h
@@ -17,6 +17,7 @@
 #ifndef _ANDROID__DATABASE_WINDOW_H
 #define _ANDROID__DATABASE_WINDOW_H
 
+#include <inttypes.h>
 #include <stddef.h>
 #include <stdint.h>
 
@@ -128,12 +129,13 @@
     inline const char* getFieldSlotValueString(FieldSlot* fieldSlot,
             size_t* outSizeIncludingNull) {
         *outSizeIncludingNull = fieldSlot->data.buffer.size;
-        return static_cast<char*>(offsetToPtr(fieldSlot->data.buffer.offset));
+        return static_cast<char*>(offsetToPtr(
+                fieldSlot->data.buffer.offset, fieldSlot->data.buffer.size));
     }
 
     inline const void* getFieldSlotValueBlob(FieldSlot* fieldSlot, size_t* outSize) {
         *outSize = fieldSlot->data.buffer.size;
-        return offsetToPtr(fieldSlot->data.buffer.offset);
+        return offsetToPtr(fieldSlot->data.buffer.offset, fieldSlot->data.buffer.size);
     }
 
 private:
@@ -166,7 +168,16 @@
     bool mReadOnly;
     Header* mHeader;
 
-    inline void* offsetToPtr(uint32_t offset) {
+    inline void* offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) {
+        if (offset >= mSize) {
+            ALOGE("Offset %" PRIu32 " out of bounds, max value %zu", offset, mSize);
+            return NULL;
+        }
+        if (offset + bufferSize > mSize) {
+            ALOGE("End offset %" PRIu32 " out of bounds, max value %zu",
+                    offset + bufferSize, mSize);
+            return NULL;
+        }
         return static_cast<uint8_t*>(mData) + offset;
     }
 
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index ff40c8a..9515b82 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -47,6 +47,7 @@
     renderthread/RenderThread.cpp \
     renderthread/TimeLord.cpp \
     renderthread/Frame.cpp \
+    service/GraphicsStatsService.cpp \
     thread/TaskManager.cpp \
     utils/Blur.cpp \
     utils/GLUtils.cpp \
@@ -293,6 +294,7 @@
     tests/unit/GlopBuilderTests.cpp \
     tests/unit/GpuMemoryTrackerTests.cpp \
     tests/unit/GradientCacheTests.cpp \
+    tests/unit/GraphicsStatsServiceTests.cpp \
     tests/unit/LayerUpdateQueueTests.cpp \
     tests/unit/LeakCheckTests.cpp \
     tests/unit/LinearAllocatorTests.cpp \
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 2132c2b..7be71ee 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -110,7 +110,7 @@
 }
 
 // Only called when dumping stats, less performance sensitive
-static uint32_t frameTimeForFrameCountIndex(uint32_t index) {
+int32_t JankTracker::frameTimeForFrameCountIndex(uint32_t index) {
     index = index + kBucketMinThreshold;
     if (index > kBucket2msIntervals) {
         index += (index - kBucket2msIntervals);
@@ -123,6 +123,10 @@
     return index;
 }
 
+int32_t JankTracker::frameTimeForSlowFrameCountIndex(uint32_t index) {
+    return (index * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs;
+}
+
 JankTracker::JankTracker(const DisplayInfo& displayInfo) {
     // By default this will use malloc memory. It may be moved later to ashmem
     // if there is shared space for it and a request comes in to do that.
@@ -161,8 +165,25 @@
     mData = nullptr;
 }
 
+void JankTracker::rotateStorage() {
+    // If we are mapped we want to stop using the ashmem backend and switch to malloc
+    // We are expecting a switchStorageToAshmem call to follow this, but it's not guaranteed
+    // If we aren't sitting on top of ashmem then just do a reset() as it's functionally
+    // equivalent do a free, malloc, reset.
+    if (mIsMapped) {
+        freeData();
+        mData = new ProfileData;
+    }
+    reset();
+}
+
 void JankTracker::switchStorageToAshmem(int ashmemfd) {
     int regionSize = ashmem_get_size_region(ashmemfd);
+    if (regionSize < 0) {
+        int err = errno;
+        ALOGW("Failed to get ashmem region size from fd %d, err %d %s", ashmemfd, err, strerror(err));
+        return;
+    }
     if (regionSize < static_cast<int>(sizeof(ProfileData))) {
         ALOGW("Ashmem region is too small! Received %d, required %u",
                 regionSize, static_cast<unsigned int>(sizeof(ProfileData)));
@@ -279,15 +300,19 @@
     }
 }
 
-void JankTracker::dumpBuffer(const void* buffer, size_t bufsize, int fd) {
-    if (bufsize < sizeof(ProfileData)) {
-        return;
+void JankTracker::dumpData(int fd, const ProfileDataDescription* description, const ProfileData* data) {
+    if (description) {
+        switch (description->type) {
+            case JankTrackerType::Generic:
+                break;
+            case JankTrackerType::Package:
+                dprintf(fd, "\nPackage: %s", description->name.c_str());
+                break;
+            case JankTrackerType::Window:
+                dprintf(fd, "\nWindow: %s", description->name.c_str());
+                break;
+        }
     }
-    const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer);
-    dumpData(data, fd);
-}
-
-void JankTracker::dumpData(const ProfileData* data, int fd) {
     if (sFrameStart != FrameInfoIndex::IntendedVsync) {
         dprintf(fd, "\nNote: Data has been filtered!");
     }
@@ -308,7 +333,7 @@
                 data->frameCounts[i]);
     }
     for (size_t i = 0; i < data->slowFrameCounts.size(); i++) {
-        dprintf(fd, " %zums=%u", (i * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs,
+        dprintf(fd, " %ums=%u", frameTimeForSlowFrameCountIndex(i),
                 data->slowFrameCounts[i]);
     }
     dprintf(fd, "\n");
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 8b482d5..6ff5d89 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -54,29 +54,50 @@
     nsecs_t statStartTime;
 };
 
+enum class JankTrackerType {
+    // The default, means there's no description set
+    Generic,
+    // The profile data represents a package
+    Package,
+    // The profile data is for a specific window
+    Window,
+};
+
+// Metadata about the ProfileData being collected
+struct ProfileDataDescription {
+    JankTrackerType type;
+    std::string name;
+};
+
 // TODO: Replace DrawProfiler with this
 class JankTracker {
 public:
     explicit JankTracker(const DisplayInfo& displayInfo);
     ~JankTracker();
 
+    void setDescription(JankTrackerType type, const std::string&& name) {
+        mDescription.type = type;
+        mDescription.name = name;
+    }
+
     void addFrame(const FrameInfo& frame);
 
-    void dump(int fd) { dumpData(mData, fd); }
+    void dump(int fd) { dumpData(fd, &mDescription, mData); }
     void reset();
 
+    void rotateStorage();
     void switchStorageToAshmem(int ashmemfd);
 
     uint32_t findPercentile(int p) { return findPercentile(mData, p); }
-
-    ANDROID_API static void dumpBuffer(const void* buffer, size_t bufsize, int fd);
+    static int32_t frameTimeForFrameCountIndex(uint32_t index);
+    static int32_t frameTimeForSlowFrameCountIndex(uint32_t index);
 
 private:
     void freeData();
     void setFrameInterval(nsecs_t frameIntervalNanos);
 
     static uint32_t findPercentile(const ProfileData* data, int p);
-    static void dumpData(const ProfileData* data, int fd);
+    static void dumpData(int fd, const ProfileDataDescription* description, const ProfileData* data);
 
     std::array<int64_t, NUM_BUCKETS> mThresholds;
     int64_t mFrameInterval;
@@ -90,6 +111,7 @@
     nsecs_t mDequeueTimeForgiveness = 0;
     ProfileData* mData;
     bool mIsMapped = false;
+    ProfileDataDescription mDescription;
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 09e34bf..2931255 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -211,7 +211,7 @@
         return sRenderPipelineType;
     }
     char prop[PROPERTY_VALUE_MAX];
-    property_get(PROPERTY_DEFAULT_RENDERER, prop, "opengl");
+    property_get(PROPERTY_RENDERER, prop, "opengl");
     if (!strcmp(prop, "skiagl") ) {
         sRenderPipelineType = RenderPipelineType::SkiaGL;
     } else if (!strcmp(prop, "skiavk") ) {
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 6dc0cb3..9db6449 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -156,7 +156,7 @@
  * Allows to set rendering pipeline mode to OpenGL (default), Skia OpenGL
  * or Vulkan.
  */
-#define PROPERTY_DEFAULT_RENDERER "debug.hwui.default_renderer"
+#define PROPERTY_RENDERER "debug.hwui.renderer"
 
 ///////////////////////////////////////////////////////////////////////////////
 // Runtime configuration properties
diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk
index a75fd6a..8826cfc 100644
--- a/libs/hwui/hwui_static_deps.mk
+++ b/libs/hwui/hwui_static_deps.mk
@@ -22,11 +22,12 @@
     libskia \
     libui \
     libgui \
-    libprotobuf-cpp-lite \
+    libprotobuf-cpp-full \
     libharfbuzz_ng \
     libft2 \
     libminikin \
-    libandroidfw
+    libandroidfw \
+    libRScpp
 
-# enable RENDERSCRIPT
-LOCAL_SHARED_LIBRARIES += libRScpp
+LOCAL_STATIC_LIBRARIES += \
+    libplatformprotos
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index a53e5e0..02a9ffa 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -590,6 +590,7 @@
 }
 
 void CanvasContext::dumpFrames(int fd) {
+    mJankTracker.dump(fd);
     FILE* file = fdopen(fd, "a");
     fprintf(file, "\n\n---PROFILEDATA---\n");
     for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
@@ -615,6 +616,10 @@
     mRenderThread.jankTracker().reset();
 }
 
+void CanvasContext::setName(const std::string&& name) {
+    mJankTracker.setDescription(JankTrackerType::Window, std::move(name));
+}
+
 void CanvasContext::serializeDisplayListTree() {
 #if ENABLE_RENDERNODE_SERIALIZATION
     using namespace google::protobuf::io;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index aa01caa..738c091 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -155,8 +155,7 @@
     void dumpFrames(int fd);
     void resetFrameStats();
 
-    void setName(const std::string&& name) { mName = name; }
-    const std::string& name() { return mName; }
+    void setName(const std::string&& name);
 
     void serializeDisplayListTree();
 
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 7020be0..860725b 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -106,7 +106,10 @@
 
     // Now that extensions are loaded, pick a swap behavior
     if (Properties::enablePartialUpdates) {
-        if (Properties::useBufferAge && EglExtensions.bufferAge) {
+        // An Adreno driver bug is causing rendering problems for SkiaGL with
+        // buffer age swap behavior (b/31957043).  To temporarily workaround,
+        // we will use preserved swap behavior.
+        if (Properties::useBufferAge && EglExtensions.bufferAge && !Properties::isSkiaEnabled()) {
             mSwapBehavior = SwapBehavior::BufferAge;
         } else {
             mSwapBehavior = SwapBehavior::Preserved;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 11614fa..f4a4773 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -487,9 +487,22 @@
 
 void RenderProxy::setProcessStatsBuffer(int fd) {
     SETUP_TASK(setProcessStatsBuffer);
-    args->thread = &mRenderThread;
+    auto& rt = RenderThread::getInstance();
+    args->thread = &rt;
     args->fd = dup(fd);
-    post(task);
+    rt.queue(task);
+}
+
+CREATE_BRIDGE1(rotateProcessStatsBuffer, RenderThread* thread) {
+    args->thread->jankTracker().rotateStorage();
+    return nullptr;
+}
+
+void RenderProxy::rotateProcessStatsBuffer() {
+    SETUP_TASK(rotateProcessStatsBuffer);
+    auto& rt = RenderThread::getInstance();
+    args->thread = &rt;
+    rt.queue(task);
 }
 
 int RenderProxy::getRenderThreadTid() {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 1629090..a60ed55 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -113,7 +113,8 @@
     uint32_t frameTimePercentile(int p);
     ANDROID_API static void dumpGraphicsMemory(int fd);
 
-    ANDROID_API void setProcessStatsBuffer(int fd);
+    ANDROID_API static void rotateProcessStatsBuffer();
+    ANDROID_API static void setProcessStatsBuffer(int fd);
     ANDROID_API int getRenderThreadTid();
 
     ANDROID_API void serializeDisplayListTree();
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index d121bcf..9bc5985 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -74,6 +74,7 @@
 };
 
 class ANDROID_API RenderThread : public Thread {
+    PREVENT_COPY_AND_ASSIGN(RenderThread);
 public:
     // RenderThread takes complete ownership of tasks that are queued
     // and will delete them after they are run
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
new file mode 100644
index 0000000..ab6420e
--- /dev/null
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "GraphicsStatsService.h"
+
+#include "JankTracker.h"
+
+#include <frameworks/base/core/proto/android/service/graphicsstats.pb.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <log/log.h>
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+namespace android {
+namespace uirenderer {
+
+using namespace google::protobuf;
+
+constexpr int32_t sCurrentFileVersion = 1;
+constexpr int32_t sHeaderSize = 4;
+static_assert(sizeof(sCurrentFileVersion) == sHeaderSize, "Header size is wrong");
+
+constexpr int sHistogramSize =
+        std::tuple_size<decltype(ProfileData::frameCounts)>::value +
+        std::tuple_size<decltype(ProfileData::slowFrameCounts)>::value;
+
+static void mergeProfileDataIntoProto(service::GraphicsStatsProto* proto,
+        const std::string& package, int versionCode, int64_t startTime, int64_t endTime,
+        const ProfileData* data);
+static void dumpAsTextToFd(service::GraphicsStatsProto* proto, int outFd);
+
+bool GraphicsStatsService::parseFromFile(const std::string& path, service::GraphicsStatsProto* output) {
+
+    int fd = open(path.c_str(), O_RDONLY);
+    if (fd == -1) {
+        int err = errno;
+        // The file not existing is normal for addToDump(), so only log if
+        // we get an unexpected error
+        if (err != ENOENT) {
+            ALOGW("Failed to open '%s', errno=%d (%s)", path.c_str(), err, strerror(err));
+        }
+        return false;
+    }
+    uint32_t file_version;
+    ssize_t bytesRead = read(fd, &file_version, sHeaderSize);
+    if (bytesRead != sHeaderSize || file_version != sCurrentFileVersion) {
+        ALOGW("Failed to read '%s', bytesRead=%zd file_version=%d", path.c_str(), bytesRead,
+                file_version);
+        close(fd);
+        return false;
+    }
+
+    io::FileInputStream input(fd);
+    bool success = output->ParseFromZeroCopyStream(&input);
+    if (input.GetErrno() != 0) {
+        ALOGW("Error reading from fd=%d, path='%s' err=%d (%s)",
+                fd, path.c_str(), input.GetErrno(), strerror(input.GetErrno()));
+        success = false;
+    } else if (!success) {
+        ALOGW("Parse failed on '%s' error='%s'",
+                path.c_str(), output->InitializationErrorString().c_str());
+    }
+    close(fd);
+    return success;
+}
+
+void mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, const std::string& package,
+        int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data) {
+    if (proto->stats_start() == 0 || proto->stats_start() > startTime) {
+        proto->set_stats_start(startTime);
+    }
+    if (proto->stats_end() == 0 || proto->stats_end() < endTime) {
+        proto->set_stats_end(endTime);
+    }
+    proto->set_package_name(package);
+    proto->set_version_code(versionCode);
+    auto summary = proto->mutable_summary();
+    summary->set_total_frames(summary->total_frames() + data->totalFrameCount);
+    summary->set_janky_frames(summary->janky_frames() + data->jankFrameCount);
+    summary->set_missed_vsync_count(
+            summary->missed_vsync_count() + data->jankTypeCounts[kMissedVsync]);
+    summary->set_high_input_latency_count(
+            summary->high_input_latency_count() + data->jankTypeCounts[kHighInputLatency]);
+    summary->set_slow_ui_thread_count(
+            summary->slow_ui_thread_count() + data->jankTypeCounts[kSlowUI]);
+    summary->set_slow_bitmap_upload_count(
+            summary->slow_bitmap_upload_count() + data->jankTypeCounts[kSlowSync]);
+    summary->set_slow_draw_count(
+            summary->slow_draw_count() + data->jankTypeCounts[kSlowRT]);
+
+    bool creatingHistogram = false;
+    if (proto->histogram_size() == 0) {
+        proto->mutable_histogram()->Reserve(sHistogramSize);
+        creatingHistogram = true;
+    } else if (proto->histogram_size() != sHistogramSize) {
+        LOG_ALWAYS_FATAL("Histogram size mismatch, proto is %d expected %d",
+                proto->histogram_size(), sHistogramSize);
+    }
+    for (size_t i = 0; i < data->frameCounts.size(); i++) {
+        service::GraphicsStatsHistogramBucketProto* bucket;
+        int32_t renderTime = JankTracker::frameTimeForFrameCountIndex(i);
+        if (creatingHistogram) {
+            bucket = proto->add_histogram();
+            bucket->set_render_millis(renderTime);
+        } else {
+            bucket = proto->mutable_histogram(i);
+            LOG_ALWAYS_FATAL_IF(bucket->render_millis() != renderTime,
+                    "Frame time mistmatch %d vs. %d", bucket->render_millis(), renderTime);
+        }
+        bucket->set_frame_count(bucket->frame_count() + data->frameCounts[i]);
+    }
+    for (size_t i = 0; i < data->slowFrameCounts.size(); i++) {
+        service::GraphicsStatsHistogramBucketProto* bucket;
+        int32_t renderTime = JankTracker::frameTimeForSlowFrameCountIndex(i);
+        if (creatingHistogram) {
+            bucket = proto->add_histogram();
+            bucket->set_render_millis(renderTime);
+        } else {
+            constexpr int offset = std::tuple_size<decltype(ProfileData::frameCounts)>::value;
+            bucket = proto->mutable_histogram(offset + i);
+            LOG_ALWAYS_FATAL_IF(bucket->render_millis() != renderTime,
+                    "Frame time mistmatch %d vs. %d", bucket->render_millis(), renderTime);
+        }
+        bucket->set_frame_count(bucket->frame_count() + data->slowFrameCounts[i]);
+    }
+}
+
+static int32_t findPercentile(service::GraphicsStatsProto* proto, int percentile) {
+    int32_t pos = percentile * proto->summary().total_frames() / 100;
+    int32_t remaining = proto->summary().total_frames() - pos;
+    for (auto it = proto->histogram().rbegin(); it != proto->histogram().rend(); ++it) {
+        remaining -= it->frame_count();
+        if (remaining <= 0) {
+            return it->render_millis();
+        }
+    }
+    return 0;
+}
+
+void dumpAsTextToFd(service::GraphicsStatsProto* proto, int fd) {
+    // This isn't a full validation, just enough that we can deref at will
+    LOG_ALWAYS_FATAL_IF(proto->package_name().empty()
+            || !proto->has_summary(), "package_name() '%s' summary %d",
+            proto->package_name().c_str(), proto->has_summary());
+    dprintf(fd, "\nPackage: %s", proto->package_name().c_str());
+    dprintf(fd, "\nVersion: %d", proto->version_code());
+    dprintf(fd, "\nStats since: %lldns", proto->stats_start());
+    dprintf(fd, "\nStats end: %lldns", proto->stats_end());
+    auto summary = proto->summary();
+    dprintf(fd, "\nTotal frames rendered: %d", summary.total_frames());
+    dprintf(fd, "\nJanky frames: %d (%.2f%%)", summary.janky_frames(),
+            (float) summary.janky_frames() / (float) summary.total_frames() * 100.0f);
+    dprintf(fd, "\n50th percentile: %dms", findPercentile(proto, 50));
+    dprintf(fd, "\n90th percentile: %dms", findPercentile(proto, 90));
+    dprintf(fd, "\n95th percentile: %dms", findPercentile(proto, 95));
+    dprintf(fd, "\n99th percentile: %dms", findPercentile(proto, 99));
+    dprintf(fd, "\nNumber Missed Vsync: %d", summary.missed_vsync_count());
+    dprintf(fd, "\nNumber High input latency: %d", summary.high_input_latency_count());
+    dprintf(fd, "\nNumber Slow UI thread: %d", summary.slow_ui_thread_count());
+    dprintf(fd, "\nNumber Slow bitmap uploads: %d", summary.slow_bitmap_upload_count());
+    dprintf(fd, "\nNumber Slow issue draw commands: %d", summary.slow_draw_count());
+    dprintf(fd, "\nHISTOGRAM:");
+    for (const auto& it : proto->histogram()) {
+        dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count());
+    }
+    dprintf(fd, "\n");
+}
+
+void GraphicsStatsService::saveBuffer(const std::string& path, const std::string& package,
+        int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data) {
+    service::GraphicsStatsProto statsProto;
+    if (!parseFromFile(path, &statsProto)) {
+        statsProto.Clear();
+    }
+    mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data);
+    // Although we might not have read any data from the file, merging the existing data
+    // should always fully-initialize the proto
+    LOG_ALWAYS_FATAL_IF(!statsProto.IsInitialized(), "%s",
+            statsProto.InitializationErrorString().c_str());
+    LOG_ALWAYS_FATAL_IF(statsProto.package_name().empty()
+            || !statsProto.has_summary(), "package_name() '%s' summary %d",
+            statsProto.package_name().c_str(), statsProto.has_summary());
+    int outFd = open(path.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0660);
+    if (outFd <= 0) {
+        int err = errno;
+        ALOGW("Failed to open '%s', error=%d (%s)", path.c_str(), err, strerror(err));
+        return;
+    }
+    int wrote = write(outFd, &sCurrentFileVersion, sHeaderSize);
+    if (wrote != sHeaderSize) {
+        int err = errno;
+        ALOGW("Failed to write header to '%s', returned=%d errno=%d (%s)",
+                path.c_str(), wrote, err, strerror(err));
+        close(outFd);
+        return;
+    }
+    {
+        io::FileOutputStream output(outFd);
+        bool success = statsProto.SerializeToZeroCopyStream(&output) && output.Flush();
+        if (output.GetErrno() != 0) {
+            ALOGW("Error writing to fd=%d, path='%s' err=%d (%s)",
+                    outFd, path.c_str(), output.GetErrno(), strerror(output.GetErrno()));
+            success = false;
+        } else if (!success) {
+            ALOGW("Serialize failed on '%s' unknown error", path.c_str());
+        }
+    }
+    close(outFd);
+}
+
+class GraphicsStatsService::Dump {
+public:
+    Dump(int outFd, DumpType type) : mFd(outFd), mType(type) {}
+    int fd() { return mFd; }
+    DumpType type() { return mType; }
+    service::GraphicsStatsServiceDumpProto& proto() { return mProto; }
+private:
+    int mFd;
+    DumpType mType;
+    service::GraphicsStatsServiceDumpProto mProto;
+};
+
+GraphicsStatsService::Dump* GraphicsStatsService::createDump(int outFd, DumpType type) {
+    return new Dump(outFd, type);
+}
+
+void GraphicsStatsService::addToDump(Dump* dump, const std::string& path, const std::string& package,
+        int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data) {
+    service::GraphicsStatsProto statsProto;
+    if (!path.empty() && !parseFromFile(path, &statsProto)) {
+        statsProto.Clear();
+    }
+    if (data) {
+        mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data);
+    }
+    if (!statsProto.IsInitialized()) {
+        ALOGW("Failed to load profile data from path '%s' and data %p",
+                path.empty() ? "<empty>" : path.c_str(), data);
+        return;
+    }
+
+    if (dump->type() == DumpType::Protobuf) {
+        dump->proto().add_stats()->CopyFrom(statsProto);
+    } else {
+        dumpAsTextToFd(&statsProto, dump->fd());
+    }
+}
+
+void GraphicsStatsService::addToDump(Dump* dump, const std::string& path) {
+    service::GraphicsStatsProto statsProto;
+    if (!parseFromFile(path, &statsProto)) {
+        return;
+    }
+    if (dump->type() == DumpType::Protobuf) {
+        dump->proto().add_stats()->CopyFrom(statsProto);
+    } else {
+        dumpAsTextToFd(&statsProto, dump->fd());
+    }
+}
+
+void GraphicsStatsService::finishDump(Dump* dump) {
+    if (dump->type() == DumpType::Protobuf) {
+        io::FileOutputStream stream(dump->fd());
+        dump->proto().SerializeToZeroCopyStream(&stream);
+    }
+    delete dump;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
\ No newline at end of file
diff --git a/libs/hwui/service/GraphicsStatsService.h b/libs/hwui/service/GraphicsStatsService.h
new file mode 100644
index 0000000..d0fd60e
--- /dev/null
+++ b/libs/hwui/service/GraphicsStatsService.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "JankTracker.h"
+#include "utils/Macros.h"
+
+namespace android {
+namespace service {
+class GraphicsStatsProto;
+}
+
+namespace uirenderer {
+
+/*
+ * The exported entry points used by GraphicsStatsService.java in f/b/services/core
+ *
+ * NOTE: Avoid exporting a requirement on the protobuf itself. Keep the usage
+ * of the generated protobuf classes internal to libhwui.so to minimize library
+ * bloat.
+ */
+class GraphicsStatsService {
+public:
+    class Dump;
+    enum class DumpType {
+        Text,
+        Protobuf,
+    };
+
+    ANDROID_API static void saveBuffer(const std::string& path, const std::string& package,
+            int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data);
+
+    ANDROID_API static Dump* createDump(int outFd, DumpType type);
+    ANDROID_API static void addToDump(Dump* dump, const std::string& path, const std::string& package,
+            int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data);
+    ANDROID_API static void addToDump(Dump* dump, const std::string& path);
+    ANDROID_API static void finishDump(Dump* dump);
+
+    // Visible for testing
+    static bool parseFromFile(const std::string& path, service::GraphicsStatsProto* output);
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
new file mode 100644
index 0000000..cfe1134
--- /dev/null
+++ b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "service/GraphicsStatsService.h"
+
+#include <frameworks/base/core/proto/android/service/graphicsstats.pb.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+std::string findRootPath() {
+    char path[1024];
+    ssize_t r = readlink("/proc/self/exe", path, 1024);
+    // < 1023 because we need room for the null terminator
+    if (r <= 0 || r > 1023) {
+        int err = errno;
+        fprintf(stderr, "Failed to read from /proc/self/exe; r=%zd, err=%d (%s)\n",
+                r, err, strerror(err));
+        exit(EXIT_FAILURE);
+    }
+    while (--r > 0) {
+        if (path[r] == '/') {
+            path[r] = '\0';
+            return std::string(path);
+        }
+    }
+    return std::string();
+}
+
+// No code left untested
+TEST(GraphicsStats, findRootPath) {
+    std::string expected = "/data/nativetest/hwui_unit_tests";
+    EXPECT_EQ(expected, findRootPath());
+}
+
+TEST(GraphicsStats, saveLoad) {
+    std::string path = findRootPath() + "/test_saveLoad";
+    std::string packageName = "com.test.saveLoad";
+    ProfileData mockData;
+    mockData.jankFrameCount = 20;
+    mockData.totalFrameCount = 100;
+    mockData.statStartTime = 10000;
+    // Fill with patterned data we can recognize but which won't map to a
+    // memset or basic for iteration count
+    for (size_t i = 0; i < mockData.frameCounts.size(); i++) {
+        mockData.frameCounts[i] = ((i % 10) + 1) * 2;
+    }
+    for (size_t i = 0; i < mockData.slowFrameCounts.size(); i++) {
+        mockData.slowFrameCounts[i] = (i % 5) + 1;
+    }
+    GraphicsStatsService::saveBuffer(path, packageName, 5, 3000, 7000, &mockData);
+    service::GraphicsStatsProto loadedProto;
+    EXPECT_TRUE(GraphicsStatsService::parseFromFile(path, &loadedProto));
+    // Clean up the file
+    unlink(path.c_str());
+
+    EXPECT_EQ(packageName, loadedProto.package_name());
+    EXPECT_EQ(5, loadedProto.version_code());
+    EXPECT_EQ(3000, loadedProto.stats_start());
+    EXPECT_EQ(7000, loadedProto.stats_end());
+    // ASSERT here so we don't continue with a nullptr deref crash if this is false
+    ASSERT_TRUE(loadedProto.has_summary());
+    EXPECT_EQ(20, loadedProto.summary().janky_frames());
+    EXPECT_EQ(100, loadedProto.summary().total_frames());
+    EXPECT_EQ(mockData.frameCounts.size() + mockData.slowFrameCounts.size(),
+            (size_t) loadedProto.histogram_size());
+    for (size_t i = 0; i < (size_t) loadedProto.histogram_size(); i++) {
+        int expectedCount, expectedBucket;
+        if (i < mockData.frameCounts.size()) {
+            expectedCount = ((i % 10) + 1) * 2;
+            expectedBucket = JankTracker::frameTimeForFrameCountIndex(i);
+        } else {
+            int temp = i - mockData.frameCounts.size();
+            expectedCount = (temp % 5) + 1;
+            expectedBucket = JankTracker::frameTimeForSlowFrameCountIndex(temp);
+        }
+        EXPECT_EQ(expectedCount, loadedProto.histogram().Get(i).frame_count());
+        EXPECT_EQ(expectedBucket, loadedProto.histogram().Get(i).render_millis());
+    }
+}
+
+TEST(GraphicsStats, merge) {
+    std::string path = findRootPath() + "/test_merge";
+    std::string packageName = "com.test.merge";
+    ProfileData mockData;
+    mockData.jankFrameCount = 20;
+    mockData.totalFrameCount = 100;
+    mockData.statStartTime = 10000;
+    // Fill with patterned data we can recognize but which won't map to a
+    // memset or basic for iteration count
+    for (size_t i = 0; i < mockData.frameCounts.size(); i++) {
+        mockData.frameCounts[i] = ((i % 10) + 1) * 2;
+    }
+    for (size_t i = 0; i < mockData.slowFrameCounts.size(); i++) {
+        mockData.slowFrameCounts[i] = (i % 5) + 1;
+    }
+    GraphicsStatsService::saveBuffer(path, packageName, 5, 3000, 7000, &mockData);
+    mockData.jankFrameCount = 50;
+    mockData.totalFrameCount = 500;
+    for (size_t i = 0; i < mockData.frameCounts.size(); i++) {
+        mockData.frameCounts[i] = (i % 5) + 1;
+    }
+    for (size_t i = 0; i < mockData.slowFrameCounts.size(); i++) {
+        mockData.slowFrameCounts[i] = ((i % 10) + 1) * 2;
+    }
+    GraphicsStatsService::saveBuffer(path, packageName, 5, 7050, 10000, &mockData);
+
+    service::GraphicsStatsProto loadedProto;
+    EXPECT_TRUE(GraphicsStatsService::parseFromFile(path, &loadedProto));
+    // Clean up the file
+    unlink(path.c_str());
+
+    EXPECT_EQ(packageName, loadedProto.package_name());
+    EXPECT_EQ(5, loadedProto.version_code());
+    EXPECT_EQ(3000, loadedProto.stats_start());
+    EXPECT_EQ(10000, loadedProto.stats_end());
+    // ASSERT here so we don't continue with a nullptr deref crash if this is false
+    ASSERT_TRUE(loadedProto.has_summary());
+    EXPECT_EQ(20 + 50, loadedProto.summary().janky_frames());
+    EXPECT_EQ(100 + 500, loadedProto.summary().total_frames());
+    EXPECT_EQ(mockData.frameCounts.size() + mockData.slowFrameCounts.size(),
+            (size_t) loadedProto.histogram_size());
+    for (size_t i = 0; i < (size_t) loadedProto.histogram_size(); i++) {
+        int expectedCount, expectedBucket;
+        if (i < mockData.frameCounts.size()) {
+            expectedCount = ((i % 10) + 1) * 2;
+            expectedCount += (i % 5) + 1;
+            expectedBucket = JankTracker::frameTimeForFrameCountIndex(i);
+        } else {
+            int temp = i - mockData.frameCounts.size();
+            expectedCount = (temp % 5) + 1;
+            expectedCount += ((temp % 10) + 1) * 2;
+            expectedBucket = JankTracker::frameTimeForSlowFrameCountIndex(temp);
+        }
+        EXPECT_EQ(expectedCount, loadedProto.histogram().Get(i).frame_count());
+        EXPECT_EQ(expectedBucket, loadedProto.histogram().Get(i).render_millis());
+    }
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 3b74ee7..35988d4 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -694,10 +694,16 @@
          *            {@link TvInputService}.
          */
         public Builder(Context context, ComponentName component) {
-            mContext = context;
+            if (context == null) {
+                throw new IllegalArgumentException("context cannot be null.");
+            }
             Intent intent = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
             mResolveInfo = context.getPackageManager().resolveService(intent,
                     PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+            if (mResolveInfo == null) {
+                throw new IllegalArgumentException("Invalid component. Can't find the service.");
+            }
+            mContext = context;
         }
 
         /**
diff --git a/packages/ExtServices/Android.mk b/packages/ExtServices/Android.mk
index e8a4007..d0c2b9f 100644
--- a/packages/ExtServices/Android.mk
+++ b/packages/ExtServices/Android.mk
@@ -34,7 +34,8 @@
 
 include $(BUILD_PACKAGE)
 
-
-
-
+# Use the following include to make our test apk.
+ifeq ($(strip $(LOCAL_PACKAGE_OVERRIDES)),)
+include $(call all-makefiles-under, $(LOCAL_PATH))
+endif
 
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index f442219..f3d8983 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -25,6 +25,13 @@
         android:defaultToDeviceProtectedStorage="true"
         android:directBootAware="true">
 
+        <service android:name=".storage.CacheQuotaServiceImpl"
+             android:permission="android.permission.BIND_CACHE_QUOTA_SERVICE">
+            <intent-filter>
+                <action android:name="android.app.usage.CacheQuotaService" />
+            </intent-filter>
+        </service>
+
         <library android:name="android.ext.services"/>
     </application>
 
diff --git a/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java b/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java
new file mode 100644
index 0000000..18863ca
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java
@@ -0,0 +1,144 @@
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.ext.services.storage;
+
+import android.app.usage.CacheQuotaHint;
+import android.app.usage.CacheQuotaService;
+import android.os.Environment;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.util.ArrayMap;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * CacheQuotaServiceImpl implements the CacheQuotaService with a strategy for populating the quota
+ * of {@link CacheQuotaHint}.
+ */
+public class CacheQuotaServiceImpl extends CacheQuotaService {
+    private static final double CACHE_RESERVE_RATIO = 0.15;
+
+    @Override
+    public List<CacheQuotaHint> onComputeCacheQuotaHints(List<CacheQuotaHint> requests) {
+        ArrayMap<String, List<CacheQuotaHint>> byUuid = new ArrayMap<>();
+        final int requestCount = requests.size();
+        for (int i = 0; i < requestCount; i++) {
+            CacheQuotaHint request = requests.get(i);
+            String uuid = request.getVolumeUuid();
+            List<CacheQuotaHint> listForUuid = byUuid.get(uuid);
+            if (listForUuid == null) {
+                listForUuid = new ArrayList<>();
+                byUuid.put(uuid, listForUuid);
+            }
+            listForUuid.add(request);
+        }
+
+        List<CacheQuotaHint> processed = new ArrayList<>();
+        byUuid.entrySet().forEach(
+                requestListEntry -> {
+                    // Collapse all usage stats to the same uid.
+                    Map<Integer, List<CacheQuotaHint>> byUid = requestListEntry.getValue()
+                            .stream()
+                            .collect(Collectors.groupingBy(CacheQuotaHint::getUid));
+                    byUid.values().forEach(uidGroupedList -> {
+                        int size = uidGroupedList.size();
+                        if (size < 2) {
+                            return;
+                        }
+                        CacheQuotaHint first = uidGroupedList.get(0);
+                        for (int i = 1; i < size; i++) {
+                            /* Note: We can't use the UsageStats built-in addition function because
+                                     UIDs may span multiple packages and usage stats adding has
+                                     matching package names as a precondition. */
+                            first.getUsageStats().mTotalTimeInForeground +=
+                                    uidGroupedList.get(i).getUsageStats().mTotalTimeInForeground;
+                        }
+                    });
+
+                    // Because the foreground stats have been added to the first element, we need
+                    // a list of only the first values (which contain the merged foreground time).
+                    List<CacheQuotaHint> flattenedRequests =
+                            byUid.values()
+                                 .stream()
+                                 .map(entryList -> entryList.get(0))
+                                 .filter(entry -> entry.getUsageStats().mTotalTimeInForeground != 0)
+                                 .sorted(sCacheQuotaRequestComparator)
+                                 .collect(Collectors.toList());
+
+                    // Because the elements are sorted, we can use the index to also be the sorted
+                    // index for cache quota calculation.
+                    double sum = getSumOfFairShares(flattenedRequests.size());
+                    String uuid = requestListEntry.getKey();
+                    long reservedSize = getReservedCacheSize(uuid);
+                    for (int count = 0; count < flattenedRequests.size(); count++) {
+                        double share = getFairShareForPosition(count) / sum;
+                        CacheQuotaHint entry = flattenedRequests.get(count);
+                        CacheQuotaHint.Builder builder = new CacheQuotaHint.Builder(entry);
+                        builder.setQuota(Math.round(share * reservedSize));
+                        processed.add(builder.build());
+                    }
+                }
+        );
+
+        return processed.stream()
+                .filter(request -> request.getQuota() > 0).collect(Collectors.toList());
+    }
+
+    private double getFairShareForPosition(int position) {
+        double value = 1.0 / Math.log(position + 3) - 0.285;
+        return (value > 0.01) ? value : 0.01;
+    }
+
+    private double getSumOfFairShares(int size) {
+        double sum = 0;
+        for (int i = 0; i < size; i++) {
+            sum += getFairShareForPosition(i);
+        }
+        return sum;
+    }
+
+    private long getReservedCacheSize(String uuid) {
+        // TODO: Revisit the cache size after running more storage tests.
+        // TODO: Figure out how to ensure ExtServices has the permissions to call
+        //       StorageStatsManager, because this is ignoring the cache...
+        StorageManager storageManager = getSystemService(StorageManager.class);
+        long freeBytes = 0;
+        if (uuid == StorageManager.UUID_PRIVATE_INTERNAL) { // regular equals because of null
+            freeBytes = Environment.getDataDirectory().getFreeSpace();
+        } else {
+            final VolumeInfo vol = storageManager.findVolumeByUuid(uuid);
+            freeBytes = vol.getPath().getFreeSpace();
+        }
+        return Math.round(freeBytes * CACHE_RESERVE_RATIO);
+    }
+
+    // Compares based upon foreground time.
+    private static Comparator<CacheQuotaHint> sCacheQuotaRequestComparator =
+            new Comparator<CacheQuotaHint>() {
+        @Override
+        public int compare(CacheQuotaHint o, CacheQuotaHint t1) {
+            long x = t1.getUsageStats().getTotalTimeInForeground();
+            long y = o.getUsageStats().getTotalTimeInForeground();
+            return (x < y) ? -1 : ((x == y) ? 0 : 1);
+        }
+    };
+}
diff --git a/packages/ExtServices/tests/Android.mk b/packages/ExtServices/tests/Android.mk
new file mode 100644
index 0000000..cb3c352
--- /dev/null
+++ b/packages/ExtServices/tests/Android.mk
@@ -0,0 +1,24 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    mockito-target-minus-junit4 \
+    espresso-core \
+    truth-prebuilt \
+    legacy-android-test
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := ExtServicesUnitTests
+
+LOCAL_INSTRUMENTATION_FOR := ExtServices
+
+include $(BUILD_PACKAGE)
diff --git a/packages/ExtServices/tests/AndroidManifest.xml b/packages/ExtServices/tests/AndroidManifest.xml
new file mode 100644
index 0000000..e6c7b97
--- /dev/null
+++ b/packages/ExtServices/tests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.ext.services.tests.unit">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.ext.services"
+                     android:label="ExtServices Test Cases">
+    </instrumentation>
+
+</manifest>
\ No newline at end of file
diff --git a/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java
new file mode 100644
index 0000000..cc1699a
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.ext.services.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.app.usage.CacheQuotaHint;
+import android.app.usage.UsageStats;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.test.ServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CacheQuotaServiceImplTest extends ServiceTestCase<CacheQuotaServiceImpl> {
+    private static final String sTestVolUuid = "uuid";
+    private static final String sSecondTestVolUuid = "otherUuid";
+
+    @Mock private Context mContext;
+    @Mock private File mFile;
+    @Mock private VolumeInfo mVolume;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS) private StorageManager mStorageManager;
+
+    public CacheQuotaServiceImplTest() {
+        super(CacheQuotaServiceImpl.class);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mContext = Mockito.spy(new ContextWrapper(getSystemContext()));
+        setContext(mContext);
+        when(mContext.getSystemService(Context.STORAGE_SERVICE)).thenReturn(mStorageManager);
+
+        when(mFile.getFreeSpace()).thenReturn(10000L);
+        when(mVolume.getPath()).thenReturn(mFile);
+        when(mStorageManager.findVolumeByUuid(sTestVolUuid)).thenReturn(mVolume);
+        when(mStorageManager.findVolumeByUuid(sSecondTestVolUuid)).thenReturn(mVolume);
+
+        Intent intent = new Intent(getContext(), CacheQuotaServiceImpl.class);
+        startService(intent);
+    }
+
+    @Test
+    public void testNoApps() {
+        CacheQuotaServiceImpl service = getService();
+        assertEquals(service.onComputeCacheQuotaHints(new ArrayList()).size(), 0);
+    }
+
+    @Test
+    public void testOneApp() throws Exception {
+        ArrayList<CacheQuotaHint> requests = new ArrayList<>();
+        CacheQuotaHint request = makeNewRequest("com.test", sTestVolUuid, 1001, 100L);
+        requests.add(request);
+
+        List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests);
+
+        assertThat(output).hasSize(1);
+        assertThat(output.get(0).getQuota()).isEqualTo(1500L);
+    }
+
+    @Test
+    public void testTwoAppsOneVolume() throws Exception {
+        ArrayList<CacheQuotaHint> requests = new ArrayList<>();
+        requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L));
+        requests.add(makeNewRequest("com.test2", sTestVolUuid, 1002, 99L));
+
+        List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests);
+
+        // Note that the sizes are just the cache area split up.
+        assertThat(output).hasSize(2);
+        assertThat(output.get(0).getQuota()).isEqualTo(883);
+        assertThat(output.get(1).getQuota()).isEqualTo(1500 - 883);
+    }
+
+    @Test
+    public void testTwoAppsTwoVolumes() throws Exception {
+        ArrayList<CacheQuotaHint> requests = new ArrayList<>();
+        requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L));
+        requests.add(makeNewRequest("com.test2", sSecondTestVolUuid, 1002, 99L));
+
+        List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests);
+
+        assertThat(output).hasSize(2);
+        assertThat(output.get(0).getQuota()).isEqualTo(1500);
+        assertThat(output.get(1).getQuota()).isEqualTo(1500);
+    }
+
+    @Test
+    public void testMultipleAppsPerUidIsCollated() throws Exception {
+        ArrayList<CacheQuotaHint> requests = new ArrayList<>();
+        requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L));
+        requests.add(makeNewRequest("com.test2", sTestVolUuid, 1001, 99L));
+
+        List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests);
+
+        assertThat(output).hasSize(1);
+        assertThat(output.get(0).getQuota()).isEqualTo(1500);
+    }
+
+    @Test
+    public void testTwoAppsTwoVolumesTwoUuidsShouldBESeparate() throws Exception {
+        ArrayList<CacheQuotaHint> requests = new ArrayList<>();
+        requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L));
+        requests.add(makeNewRequest("com.test2", sSecondTestVolUuid, 1001, 99L));
+
+        List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests);
+
+        assertThat(output).hasSize(2);
+        assertThat(output.get(0).getQuota()).isEqualTo(1500);
+        assertThat(output.get(1).getQuota()).isEqualTo(1500);
+    }
+
+    private CacheQuotaHint makeNewRequest(String packageName, String uuid, int uid, long foregroundTime) {
+        UsageStats stats = new UsageStats();
+        stats.mPackageName = packageName;
+        stats.mTotalTimeInForeground = foregroundTime;
+        return new CacheQuotaHint.Builder()
+                .setVolumeUuid(uuid).setUid(uid).setUsageStats(stats).setQuota(-1).build();
+    }
+}
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index eb64b3a..d207c35 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -444,6 +444,18 @@
         <item>show_deuteranomaly</item>
     </string-array>
 
+    <!-- Titles for debug renderer preference. [CHAR LIMIT=50] -->
+    <string-array name="debug_hw_renderer_entries">
+        <item>OpenGL (Default)</item>
+        <item>OpenGL (Skia)</item>
+    </string-array>
+
+    <!-- Values for debug renderer preference. -->
+    <string-array name="debug_hw_renderer_values" translatable="false" >
+        <item>opengl</item>
+        <item>skiagl</item>
+    </string-array>
+
     <!-- Titles for app process limit preference. [CHAR LIMIT=35] -->
     <string-array name="app_process_limit_entries">
         <item>Standard limit</item>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a4824ad..961d0e5 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -586,6 +586,9 @@
     <!-- UI debug setting: show the amount of overdraw in apps using the GPU [CHAR LIMIT=25] -->
     <string name="debug_hw_overdraw">Debug GPU overdraw</string>
 
+    <!-- UI debug setting: select the renderer to use by RenderThread [CHAR LIMIT=25] -->
+    <string name="debug_hw_renderer">Set GPU Renderer</string>
+
     <!-- UI debug setting: disable use of overlays? [CHAR LIMIT=25] -->
     <string name="disable_overlays">Disable HW overlays</string>
     <!-- UI debug setting: disable use of overlays summary [CHAR LIMIT=50] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java b/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
index fa1f91f..22f8856 100644
--- a/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
@@ -38,6 +38,7 @@
     public long remainingTimeUs = 0;
     public String batteryPercentString;
     public String remainingLabel;
+    public String statusLabel;
     private BatteryStats mStats;
     private boolean mCharging;
     private long timePeriod;
@@ -135,6 +136,7 @@
         info.batteryPercentString = Utils.formatPercentage(info.mBatteryLevel);
         info.mCharging = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
         final Resources resources = context.getResources();
+        info.statusLabel = Utils.getBatteryStatus(resources, batteryBroadcast, shortString);
         if (!info.mCharging) {
             final long drainTime = stats.computeBatteryTimeRemaining(elapsedRealtimeUs);
             if (drainTime > 0) {
@@ -155,8 +157,6 @@
             }
         } else {
             final long chargeTime = stats.computeChargeTimeRemaining(elapsedRealtimeUs);
-            final String statusLabel = Utils.getBatteryStatus(
-                    resources, batteryBroadcast, shortString);
             final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
                     BatteryManager.BATTERY_STATUS_UNKNOWN);
             if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
@@ -184,9 +184,9 @@
                 info.mChargeLabelString = resources.getString(
                         resId, info.batteryPercentString, timeString);
             } else {
-                info.remainingLabel = statusLabel;
+                info.remainingLabel = null;
                 info.mChargeLabelString = resources.getString(
-                        R.string.power_charging, info.batteryPercentString, statusLabel);
+                        R.string.power_charging, info.batteryPercentString, info.statusLabel);
             }
         }
         return info;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 0ad4fbf..2dcbf90 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -766,11 +766,7 @@
     }
 
     void loadConfig(WifiConfiguration config) {
-        if (config.isPasspoint())
-            ssid = config.providerFriendlyName;
-        else
-            ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
-
+        ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
         bssid = config.BSSID;
         security = getSecurity(config);
         networkId = config.networkId;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 9ac4d2d..11bcdca 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -469,29 +469,22 @@
                 }
                 AccessPoint accessPoint = getCachedOrCreate(config, cachedAccessPoints);
                 if (mLastInfo != null && mLastNetworkInfo != null) {
-                    if (config.isPasspoint() == false) {
-                        accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
-                    }
+                    accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
                 }
                 if (mIncludeSaved) {
-                    if (!config.isPasspoint() || mIncludePasspoints) {
-                        // If saved network not present in scan result then set its Rssi to MAX_VALUE
-                        boolean apFound = false;
-                        for (ScanResult result : results) {
-                            if (result.SSID.equals(accessPoint.getSsidStr())) {
-                                apFound = true;
-                                break;
-                            }
+                    // If saved network not present in scan result then set its Rssi to MAX_VALUE
+                    boolean apFound = false;
+                    for (ScanResult result : results) {
+                        if (result.SSID.equals(accessPoint.getSsidStr())) {
+                            apFound = true;
+                            break;
                         }
-                        if (!apFound) {
-                            accessPoint.setRssi(Integer.MAX_VALUE);
-                        }
-                        accessPoints.add(accessPoint);
                     }
-
-                    if (config.isPasspoint() == false) {
-                        apMap.put(accessPoint.getSsidStr(), accessPoint);
+                    if (!apFound) {
+                        accessPoint.setRssi(Integer.MAX_VALUE);
                     }
+                    accessPoints.add(accessPoint);
+                    apMap.put(accessPoint.getSsidStr(), accessPoint);
                 } else {
                     // If we aren't using saved networks, drop them into the cache so that
                     // we have access to their saved info.
@@ -528,20 +521,16 @@
                     }
 
                     if (result.isPasspointNetwork()) {
+                        // Retrieve a WifiConfiguration for a Passpoint provider that matches
+                        // the given ScanResult.  This is used for showing that a given AP
+                        // (ScanResult) is available via a Passpoint provider (provider friendly
+                        // name).
                         WifiConfiguration config = mWifiManager.getMatchingWifiConfig(result);
                         if (config != null) {
                             accessPoint.update(config);
                         }
                     }
 
-                    if (mLastInfo != null && mLastInfo.getBSSID() != null
-                            && mLastInfo.getBSSID().equals(result.BSSID)
-                            && connectionConfig != null && connectionConfig.isPasspoint()) {
-                    /* This network is connected via this passpoint config */
-                    /* SSID match is not going to work for it; so update explicitly */
-                        accessPoint.update(connectionConfig);
-                    }
-
                     accessPoints.add(accessPoint);
                     apMap.put(accessPoint.getSsidStr(), accessPoint);
                 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/BatteryInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/BatteryInfoTest.java
new file mode 100644
index 0000000..1364958
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/BatteryInfoTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.settingslib;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.SystemClock;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BatteryInfoTest {
+    private static final String STATUS_FULL = "Full";
+    private Intent mBatteryBroadcast;
+    @Mock
+    private BatteryStats mBatteryStats;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mBatteryBroadcast = new Intent();
+        mBatteryBroadcast.putExtra(BatteryManager.EXTRA_PLUGGED, 0);
+        mBatteryBroadcast.putExtra(BatteryManager.EXTRA_LEVEL, 0);
+        mBatteryBroadcast.putExtra(BatteryManager.EXTRA_SCALE, 100);
+        mBatteryBroadcast.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_FULL);
+
+        when(mContext.getResources().getString(R.string.battery_info_status_full))
+                .thenReturn(STATUS_FULL);
+    }
+
+    @Test
+    public void testGetBatteryInfo_HasStatusLabel() {
+        BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mBatteryBroadcast, mBatteryStats,
+                SystemClock.elapsedRealtime() * 1000, true);
+
+        assertThat(info.statusLabel).isEqualTo(STATUS_FULL);
+    }
+}
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index c40797c..ca05240 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -19,9 +19,6 @@
     <!-- thickness (width) of the navigation bar on phones that require it -->
     <dimen name="navigation_bar_size">@*android:dimen/navigation_bar_width</dimen>
 
-    <!-- Standard notification gravity -->
-    <integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer>
-
     <dimen name="docked_divider_handle_width">2dp</dimen>
     <dimen name="docked_divider_handle_height">16dp</dimen>
 
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 7e63cbf..c8ffe8f 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -18,7 +18,6 @@
 <resources>
     <!-- Standard notification width + gravity -->
     <dimen name="notification_panel_width">@dimen/standard_notification_panel_width</dimen>
-    <integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer>
 
     <!-- Diameter of outer shape drawable shown in navbar search-->
     <dimen name="navbar_search_outerring_diameter">430dip</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6e399b4..728dde0 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -201,8 +201,7 @@
     <dimen name="volume_dialog_panel_width">@dimen/standard_notification_panel_width</dimen>
 
     <!-- Gravity for the notification panel -->
-    <integer name="standard_notification_panel_layout_gravity">0x31</integer><!-- top|center_horizontal -->
-    <integer name="notification_panel_layout_gravity">0x37</integer><!-- fill_horizontal|top -->
+    <integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
 
     <!-- Height of the carrier/wifi name label -->
     <dimen name="carrier_label_height">24dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index b3a0eb7..5d57daa 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -23,7 +23,6 @@
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.PowerManager;
-import android.os.SystemClock;
 
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.systemui.SystemUIApplication;
@@ -52,7 +51,10 @@
         DozeFactory.WakeLock wakeLock = new DozeFactory.WakeLock(powerManager.newWakeLock(
                 PowerManager.PARTIAL_WAKE_LOCK, "Doze"));
 
-        DozeMachine machine = new DozeMachine(dozeService, params, wakeLock);
+        DozeMachine machine = new DozeMachine(
+                DozeScreenStatePreventingAdapter.wrapIfNeeded(dozeService, params),
+                params,
+                wakeLock);
         machine.setParts(new DozeMachine.Part[]{
                 createDozeTriggers(context, sensorManager, host, config, params, handler, wakeLock,
                         machine),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java
new file mode 100644
index 0000000..ad5897a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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.doze;
+
+import android.support.annotation.VisibleForTesting;
+import android.view.Display;
+
+import com.android.systemui.statusbar.phone.DozeParameters;
+
+/**
+ * Prevents usage of doze screen states on devices that don't support them.
+ */
+public class DozeScreenStatePreventingAdapter implements DozeMachine.Service {
+
+    private final DozeMachine.Service mInner;
+
+    @VisibleForTesting
+    DozeScreenStatePreventingAdapter(DozeMachine.Service inner) {
+        mInner = inner;
+    }
+
+    @Override
+    public void finish() {
+        mInner.finish();
+    }
+
+    @Override
+    public void setDozeScreenState(int state) {
+        if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND) {
+            state = Display.STATE_ON;
+        }
+        mInner.setDozeScreenState(state);
+    }
+
+    @Override
+    public void requestWakeUp() {
+        mInner.requestWakeUp();
+    }
+
+    /**
+     * If the device supports the doze display state, return {@code inner}. Otherwise
+     * return a new instance of {@link DozeScreenStatePreventingAdapter} wrapping {@code inner}.
+     */
+    public static DozeMachine.Service wrapIfNeeded(DozeMachine.Service inner,
+            DozeParameters params) {
+        return isNeeded(params) ? new DozeScreenStatePreventingAdapter(inner) : inner;
+    }
+
+    private static boolean isNeeded(DozeParameters params) {
+        return !params.getDisplayStateSupported();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 8de4e58..33ad7fb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -375,6 +375,10 @@
         MutableBoolean isHomeStackVisible = new MutableBoolean(true);
         if (!ssp.isRecentsActivityVisible(isHomeStackVisible)) {
             ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
+            if (runningTask == null) {
+                return;
+            }
+
             RecentsTaskLoader loader = Recents.getTaskLoader();
             sInstanceLoadPlan = loader.createLoadPlan(mContext);
             sInstanceLoadPlan.preloadRawTasks(!isHomeStackVisible.value);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 6da9e90..f55699b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -292,7 +292,7 @@
         int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity);
         FrameLayout.LayoutParams lp =
                 (FrameLayout.LayoutParams) mQsFrame.getLayoutParams();
-        if (lp.width != panelWidth) {
+        if (lp.width != panelWidth || lp.gravity != panelGravity) {
             lp.width = panelWidth;
             lp.gravity = panelGravity;
             mQsFrame.setLayoutParams(lp);
@@ -300,7 +300,7 @@
         }
 
         lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams();
-        if (lp.width != panelWidth) {
+        if (lp.width != panelWidth || lp.gravity != panelGravity) {
             lp.width = panelWidth;
             lp.gravity = panelGravity;
             mNotificationStackScroller.setLayoutParams(lp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index aebc2c9..9e93ed3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -509,7 +509,8 @@
                 if (!hasNotif(notifs, pkg, info.userId)) {
                     // TODO: Optimize by not always needing to get application info.
                     // Maybe cache non-ephemeral packages?
-                    ApplicationInfo appInfo = pm.getApplicationInfo(pkg, 0, info.userId);
+                    ApplicationInfo appInfo = pm.getApplicationInfo(pkg,
+                            PackageManager.MATCH_UNINSTALLED_PACKAGES, info.userId);
                     if (appInfo.isInstantApp()) {
                         postEphemeralNotif(pkg, info.userId, appInfo, noMan);
                     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
new file mode 100644
index 0000000..ada8ac0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 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.doze;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.view.Display;
+
+import com.android.systemui.statusbar.phone.DozeParameters;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+public class DozeScreenStatePreventingAdapterTest {
+
+    private DozeMachine.Service mInner;
+    private DozeScreenStatePreventingAdapter mWrapper;
+
+    @Before
+    public void setup() throws Exception {
+        mInner = mock(DozeMachine.Service.class);
+        mWrapper = new DozeScreenStatePreventingAdapter(mInner);
+    }
+
+    @Test
+    public void forwards_finish() throws Exception {
+        mWrapper.finish();
+        verify(mInner).finish();
+    }
+
+    @Test
+    public void forwards_setDozeScreenState_on() throws Exception {
+        mWrapper.setDozeScreenState(Display.STATE_ON);
+        verify(mInner).setDozeScreenState(Display.STATE_ON);
+    }
+
+    @Test
+    public void forwards_setDozeScreenState_off() throws Exception {
+        mWrapper.setDozeScreenState(Display.STATE_OFF);
+        verify(mInner).setDozeScreenState(Display.STATE_OFF);
+    }
+
+    @Test
+    public void forwards_setDozeScreenState_doze() throws Exception {
+        mWrapper.setDozeScreenState(Display.STATE_DOZE);
+        verify(mInner).setDozeScreenState(Display.STATE_ON);
+    }
+
+    @Test
+    public void forwards_setDozeScreenState_doze_suspend() throws Exception {
+        mWrapper.setDozeScreenState(Display.STATE_DOZE_SUSPEND);
+        verify(mInner).setDozeScreenState(Display.STATE_ON);
+    }
+
+    @Test
+    public void forwards_requestWakeUp() throws Exception {
+        mWrapper.requestWakeUp();
+        verify(mInner).requestWakeUp();
+    }
+
+    @Test
+    public void wrapIfNeeded_needed() throws Exception {
+        DozeParameters params = mock(DozeParameters.class);
+        when(params.getDisplayStateSupported()).thenReturn(false);
+
+        assertEquals(DozeScreenStatePreventingAdapter.class,
+                DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params).getClass());
+    }
+
+    @Test
+    public void wrapIfNeeded_not_needed() throws Exception {
+        DozeParameters params = mock(DozeParameters.class);
+        when(params.getDisplayStateSupported()).thenReturn(true);
+
+        assertSame(mInner, DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params));
+    }
+}
\ No newline at end of file
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index fc5a9fc..c81bd1b 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3452,6 +3452,12 @@
     // FIELD - Rank of the clicked Settings search result
     FIELD_SETTINGS_SERACH_RESULT_RANK = 842;
 
+    // OPEN: Settings > Apps > Default Apps > Assist >  Default assist
+    DEFAULT_ASSIST_PICKER = 843;
+
+    // OPEN: Settings > Apps > Default Apps > Assist >  Default voice input
+    DEFAULT_VOICE_INPUT_PICKER = 844;
+
     // ---- End O Constants, all O constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 9b55c7a..b459f74 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -16,6 +16,9 @@
 
 package com.android.server.backup;
 
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY;
+import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_OLD_VERSION;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
 
 import android.app.ActivityManager;
@@ -2340,7 +2343,7 @@
             Slog.e(TAG, "No packages named for backup request");
             sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
             monitor = monitorEvent(monitor, BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
-                    null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT);
+                    null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
             throw new IllegalArgumentException("No packages are provided for backup");
         }
 
@@ -3459,6 +3462,9 @@
                 // fail repeatedly (i.e. have proved themselves to be buggy).
                 Slog.e(TAG, "Cancel backing up " + mCurrentPackage.packageName);
                 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName);
+                mMonitor = monitorEvent(mMonitor,
+                    BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_TIMEOUT,
+                    mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null);
                 addBackupTrace(
                         "cancel of " + mCurrentPackage.packageName + ", cancelAll=" + cancelAll);
                 errorCleanup();
@@ -4555,6 +4561,11 @@
                     mPackages.add(info);
                 } catch (NameNotFoundException e) {
                     Slog.i(TAG, "Requested package " + pkg + " not found; ignoring");
+                    monitor = monitorEvent(monitor,
+                            BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_FOUND,
+                            mCurrentPackage,
+                            BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                            null);
                 }
             }
         }
@@ -4638,6 +4649,10 @@
                 if (mTransport == null) {
                     Slog.w(TAG, "Transport not present; full data backup not performed");
                     backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
+                    mMonitor = monitorEvent(mMonitor,
+                            BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT,
+                            mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+                            null);
                     return;
                 }
 
@@ -5148,7 +5163,7 @@
 
                 mMonitor = monitorEvent(mMonitor,
                         BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_TIMEOUT,
-                        mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT);
+                        mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null);
                 mIsCancelled = true;
                 // Cancel tasks spun off by this task.
                 BackupManagerService.this.handleCancel(mEphemeralToken, cancelAll);
@@ -5525,6 +5540,8 @@
         // Dedicated observer, if any
         IFullBackupRestoreObserver mObserver;
 
+        IBackupManagerMonitor mMonitor;
+
         // Where we're delivering the file data as we go
         IBackupAgent mAgent;
 
@@ -5606,11 +5623,12 @@
         }
 
         public FullRestoreEngine(BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
-                PackageInfo onlyPackage, boolean allowApks, boolean allowObbs,
-                int ephemeralOpToken) {
+                IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
+                boolean allowObbs, int ephemeralOpToken) {
             mEphemeralOpToken = ephemeralOpToken;
             mMonitorTask = monitorTask;
             mObserver = observer;
+            mMonitor = monitor;
             mOnlyPackage = onlyPackage;
             mAllowApks = allowApks;
             mAllowObbs = allowObbs;
@@ -6326,6 +6344,16 @@
                                                 } else {
                                                     Slog.i(TAG, "Data requires newer version "
                                                             + version + "; ignoring");
+                                                    ArrayList<Pair<String, String>> list =
+                                                            new ArrayList<>();
+                                                    list.add(new Pair<String, String>(
+                                                            EXTRA_LOG_OLD_VERSION,
+                                                            Integer.toString(version)));
+                                                    mMonitor = monitorEvent(mMonitor,
+                                                            LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER,
+                                                            pkgInfo,
+                                                            LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                                            list);
                                                     policy = RestorePolicy.IGNORE;
                                                 }
                                             }
@@ -8461,6 +8489,10 @@
                 RestoreDescription desc = mTransport.nextRestorePackage();
                 if (desc == null) {
                     Slog.e(TAG, "No restore metadata available; halting");
+                    mMonitor = monitorEvent(mMonitor,
+                            BackupManagerMonitor.LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE,
+                            mCurrentPackage,
+                            BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
                     mStatus = BackupTransport.TRANSPORT_ERROR;
                     executeNextState(UnifiedRestoreState.FINAL);
                     return;
@@ -8562,6 +8594,11 @@
                     // Whoops, we thought we could restore this package but it
                     // turns out not to be present.  Skip it.
                     Slog.e(TAG, "Package not present: " + pkgName);
+                    mMonitor = monitorEvent(mMonitor,
+                            BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_PRESENT,
+                            mCurrentPackage,
+                            BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                            null);
                     EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
                             "Package missing on device");
                     nextState = UnifiedRestoreState.RUNNING_QUEUE;
@@ -8631,6 +8668,9 @@
                     Slog.i(TAG, "Data exists for package " + packageName
                             + " but app has no agent; skipping");
                 }
+                mMonitor = monitorEvent(mMonitor,
+                        BackupManagerMonitor.LOG_EVENT_ID_APP_HAS_NO_AGENT, mCurrentPackage,
+                        BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null);
                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
                         "Package has no agent");
                 executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
@@ -8652,6 +8692,9 @@
                     ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
             if (mAgent == null) {
                 Slog.w(TAG, "Can't find backup agent for " + packageName);
+                mMonitor = monitorEvent(mMonitor,
+                        BackupManagerMonitor.LOG_EVENT_ID_CANT_FIND_AGENT, mCurrentPackage,
+                        BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
                         "Restore agent missing");
                 executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
@@ -8857,7 +8900,7 @@
                 EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE,
                         mCurrentPackage.packageName);
 
-                mEngine = new FullRestoreEngine(this, null, mCurrentPackage, false, false, mEphemeralOpToken);
+                mEngine = new FullRestoreEngine(this, null, mMonitor, mCurrentPackage, false, false, mEphemeralOpToken);
                 mEngineThread = new EngineThread(mEngine, mEnginePipes[0]);
 
                 ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
@@ -9001,8 +9044,10 @@
                 if (DEBUG) {
                     Slog.w(TAG, "Full-data restore target timed out; shutting down");
                 }
-                mMonitor = monitorEvent(mMonitor, BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_TIMEOUT,
-                        mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT);
+
+                mMonitor = monitorEvent(mMonitor,
+                        BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_TIMEOUT,
+                        mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null);
                 mEngineThread.handleTimeout();
 
                 IoUtils.closeQuietly(mEnginePipes[1]);
@@ -9245,8 +9290,9 @@
         public void handleCancel(boolean cancelAll) {
             removeOperation(mEphemeralOpToken);
             Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
-            mMonitor = monitorEvent(mMonitor, BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT,
-                    mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT);
+            mMonitor = monitorEvent(mMonitor,
+                    BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT,
+                    mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null);
             EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
                     mCurrentPackage.packageName, "restore timeout");
             // Handle like an agent that threw on invocation: wipe it and go on to the next
@@ -10913,7 +10959,7 @@
     }
 
     private static IBackupManagerMonitor monitorEvent(IBackupManagerMonitor monitor, int id,
-            PackageInfo pkg, int category) {
+            PackageInfo pkg, int category, ArrayList<Pair<String, String>> extras) {
         if (monitor != null) {
             try {
                 Bundle bundle = new Bundle();
@@ -10925,6 +10971,11 @@
                     bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION,
                             pkg.versionCode);
                 }
+                if (extras != null) {
+                    for (Pair<String,String> pair : extras) {
+                        bundle.putString(pair.first, pair.second);
+                    }
+                }
                 monitor.onEvent(bundle);
                 return monitor;
             } catch(RemoteException e) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4bc9bb1..0bc9d19 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -815,6 +815,8 @@
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiverAsUser(
                 mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+        mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
+                new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
 
         try {
             mNetd.registerObserver(mTethering);
@@ -4008,6 +4010,16 @@
         }
     };
 
+    private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // Try creating lockdown tracker, since user present usually means
+            // unlocked keystore.
+            updateLockdownVpn();
+            mContext.unregisterReceiver(this);
+        }
+    };
+
     private final HashMap<Messenger, NetworkFactoryInfo> mNetworkFactoryInfos =
             new HashMap<Messenger, NetworkFactoryInfo>();
     private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index ecbe1ca..bdd80e38 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -16,67 +16,145 @@
 
 package com.android.server;
 
+import android.app.AlarmManager;
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.MemoryFile;
+import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.util.Log;
 import android.view.IGraphicsStats;
-import android.view.ThreadedRenderer;
+import android.view.IGraphicsStatsCallback;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.TimeZone;
 
 /**
  * This service's job is to collect aggregate rendering profile data. It
  * does this by allowing rendering processes to request an ashmem buffer
- * to place their stats into. This buffer will be pre-initialized with historical
- * data for that process if it exists (if the userId & packageName match a buffer
- * in the historical log)
+ * to place their stats into.
  *
- * This service does not itself attempt to understand the data in the buffer,
- * its primary job is merely to manage distributing these buffers. However,
- * it is assumed that this buffer is for ThreadedRenderer and delegates
- * directly to ThreadedRenderer for dumping buffers.
+ * Buffers are rotated on a daily (in UTC) basis and only the 3 most-recent days
+ * are kept.
  *
- * MEMORY USAGE:
+ * The primary consumer of this is incident reports and automated metric checking. It is not
+ * intended for end-developer consumption, for that we have gfxinfo.
  *
- * This class consumes UP TO:
- * 1) [active rendering processes] * (ASHMEM_SIZE * 2)
- * 2) ASHMEM_SIZE (for scratch space used during dumping)
- * 3) ASHMEM_SIZE * HISTORY_SIZE
- *
- * This is currently under 20KiB total memory in the worst case of
- * 20 processes in history + 10 unique active processes.
+ * Buffer rotation process:
+ * 1) Alarm fires
+ * 2) onRotateGraphicsStatsBuffer() is sent to all active processes
+ * 3) Upon receiving the callback, the process will stop using the previous ashmem buffer and
+ *    request a new one.
+ * 4) When that request is received we now know that the ashmem region is no longer in use so
+ *    it gets queued up for saving to disk and a new ashmem region is created and returned
+ *    for the process to use.
  *
  *  @hide */
 public class GraphicsStatsService extends IGraphicsStats.Stub {
     public static final String GRAPHICS_STATS_SERVICE = "graphicsstats";
 
     private static final String TAG = "GraphicsStatsService";
-    private static final int ASHMEM_SIZE = 464;
-    private static final int HISTORY_SIZE = 20;
+
+    private static final int SAVE_BUFFER = 1;
+    private static final int DELETE_OLD = 2;
+
+    // This isn't static because we need this to happen after registerNativeMethods, however
+    // the class is loaded (and thus static ctor happens) before that occurs.
+    private final int ASHMEM_SIZE = nGetAshmemSize();
+    private final byte[] ZERO_DATA = new byte[ASHMEM_SIZE];
 
     private final Context mContext;
     private final AppOpsManager mAppOps;
+    private final AlarmManager mAlarmManager;
     private final Object mLock = new Object();
     private ArrayList<ActiveBuffer> mActive = new ArrayList<>();
-    private HistoricalData[] mHistoricalLog = new HistoricalData[HISTORY_SIZE];
-    private int mNextHistoricalSlot = 0;
-    private byte[] mTempBuffer = new byte[ASHMEM_SIZE];
+    private File mGraphicsStatsDir;
+    private final Object mFileAccessLock = new Object();
+    private Handler mWriteOutHandler;
+    private boolean mRotateIsScheduled = false;
 
     public GraphicsStatsService(Context context) {
         mContext = context;
         mAppOps = context.getSystemService(AppOpsManager.class);
+        mAlarmManager = context.getSystemService(AlarmManager.class);
+        File systemDataDir = new File(Environment.getDataDirectory(), "system");
+        mGraphicsStatsDir = new File(systemDataDir, "graphicsstats");
+        mGraphicsStatsDir.mkdirs();
+        if (!mGraphicsStatsDir.exists()) {
+            throw new IllegalStateException("Graphics stats directory does not exist: "
+                    + mGraphicsStatsDir.getAbsolutePath());
+        }
+        HandlerThread bgthread = new HandlerThread("GraphicsStats-disk", Process.THREAD_PRIORITY_BACKGROUND);
+        bgthread.start();
+
+        mWriteOutHandler = new Handler(bgthread.getLooper(), new Handler.Callback() {
+            @Override
+            public boolean handleMessage(Message msg) {
+                switch (msg.what) {
+                    case SAVE_BUFFER:
+                        saveBuffer((HistoricalBuffer) msg.obj);
+                        break;
+                    case DELETE_OLD:
+                        deleteOldBuffers();
+                        break;
+                }
+                return true;
+            }
+        });
+    }
+
+    /**
+     * Current rotation policy is to rotate at midnight UTC. We don't specify RTC_WAKEUP because
+     * rotation can be delayed if there's otherwise no activity. However exact is used because
+     * we don't want the system to delay it by TOO much.
+     */
+    private void scheduleRotateLocked() {
+        if (mRotateIsScheduled) {
+            return;
+        }
+        mRotateIsScheduled = true;
+        Calendar calendar = normalizeDate(System.currentTimeMillis());
+        calendar.add(Calendar.DATE, 1);
+        mAlarmManager.setExact(AlarmManager.RTC, calendar.getTimeInMillis(), TAG, this::onAlarm,
+                mWriteOutHandler);
+    }
+
+    private void onAlarm() {
+        synchronized (mLock) {
+            mRotateIsScheduled = false;
+            scheduleRotateLocked();
+            for (ActiveBuffer active : mActive) {
+                try {
+                    active.mCallback.onRotateGraphicsStatsBuffer();
+                } catch (RemoteException e) {
+                    Log.w(TAG, String.format("Failed to notify '%s' (pid=%d) to rotate buffers",
+                            active.mInfo.packageName, active.mPid), e);
+                }
+            }
+        }
+        // Give a few seconds for everyone to rotate before doing the cleanup
+        mWriteOutHandler.sendEmptyMessageDelayed(DELETE_OLD, 10000);
     }
 
     @Override
-    public ParcelFileDescriptor requestBufferForProcess(String packageName, IBinder token)
+    public ParcelFileDescriptor requestBufferForProcess(String packageName, IGraphicsStatsCallback token)
             throws RemoteException {
         int uid = Binder.getCallingUid();
         int pid = Binder.getCallingPid();
@@ -84,9 +162,12 @@
         long callingIdentity = Binder.clearCallingIdentity();
         try {
             mAppOps.checkPackage(uid, packageName);
+            PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName, 0);
             synchronized (mLock) {
-                pfd = requestBufferForProcessLocked(token, uid, pid, packageName);
+                pfd = requestBufferForProcessLocked(token, uid, pid, packageName, info.versionCode);
             }
+        } catch (PackageManager.NameNotFoundException ex) {
+            throw new RemoteException("Unable to find package: '" + packageName + "'");
         } finally {
             Binder.restoreCallingIdentity(callingIdentity);
         }
@@ -95,51 +176,130 @@
 
     private ParcelFileDescriptor getPfd(MemoryFile file) {
         try {
+            if (!file.getFileDescriptor().valid()) {
+                throw new IllegalStateException("Invalid file descriptor");
+            }
             return new ParcelFileDescriptor(file.getFileDescriptor());
         } catch (IOException ex) {
             throw new IllegalStateException("Failed to get PFD from memory file", ex);
         }
     }
 
-    private ParcelFileDescriptor requestBufferForProcessLocked(IBinder token,
-            int uid, int pid, String packageName) throws RemoteException {
-        ActiveBuffer buffer = fetchActiveBuffersLocked(token, uid, pid, packageName);
+    private ParcelFileDescriptor requestBufferForProcessLocked(IGraphicsStatsCallback token,
+            int uid, int pid, String packageName, int versionCode) throws RemoteException {
+        ActiveBuffer buffer = fetchActiveBuffersLocked(token, uid, pid, packageName, versionCode);
+        scheduleRotateLocked();
         return getPfd(buffer.mProcessBuffer);
     }
 
+    private Calendar normalizeDate(long timestamp) {
+        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+        calendar.setTimeInMillis(timestamp);
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        return calendar;
+    }
+
+    private File pathForApp(BufferInfo info) {
+        String subPath = String.format("%d/%s/%d/total",
+                normalizeDate(info.startTime).getTimeInMillis(), info.packageName, info.versionCode);
+        return new File(mGraphicsStatsDir, subPath);
+    }
+
+    private void saveBuffer(HistoricalBuffer buffer) {
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "saving graphicsstats for " + buffer.mInfo.packageName);
+        }
+        synchronized (mFileAccessLock) {
+            File path = pathForApp(buffer.mInfo);
+            File parent = path.getParentFile();
+            parent.mkdirs();
+            if (!parent.exists()) {
+                Log.w(TAG, "Unable to create path: '" + parent.getAbsolutePath() + "'");
+                return;
+            }
+            nSaveBuffer(path.getAbsolutePath(), buffer.mInfo.packageName, buffer.mInfo.versionCode,
+                    buffer.mInfo.startTime, buffer.mInfo.endTime, buffer.mData);
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+    }
+
+    private void deleteRecursiveLocked(File file) {
+        if (file.isDirectory()) {
+            for (File child : file.listFiles()) {
+                deleteRecursiveLocked(child);
+            }
+        }
+        if (!file.delete()) {
+            Log.w(TAG, "Failed to delete '" + file.getAbsolutePath() + "'!");
+        }
+    }
+
+    private void deleteOldBuffers() {
+        Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "deleting old graphicsstats buffers");
+        synchronized (mFileAccessLock) {
+            File[] files = mGraphicsStatsDir.listFiles();
+            if (files == null || files.length <= 3) {
+                return;
+            }
+            long[] sortedDates = new long[files.length];
+            for (int i = 0; i < files.length; i++) {
+                try {
+                    sortedDates[i] = Long.parseLong(files[i].getName());
+                } catch (NumberFormatException ex) {
+                    // Skip unrecognized folders
+                }
+            }
+            if (sortedDates.length <= 3) {
+                return;
+            }
+            Arrays.sort(sortedDates);
+            for (int i = 0; i < sortedDates.length - 3; i++) {
+                deleteRecursiveLocked(new File(mGraphicsStatsDir, Long.toString(sortedDates[i])));
+            }
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+    }
+
+    private void addToSaveQueue(ActiveBuffer buffer) {
+        try {
+            HistoricalBuffer data = new HistoricalBuffer(buffer);
+            Message.obtain(mWriteOutHandler, SAVE_BUFFER, data).sendToTarget();
+        } catch (IOException e) {
+            Log.w(TAG, "Failed to copy graphicsstats from " + buffer.mInfo.packageName, e);
+        }
+        buffer.closeAllBuffers();
+    }
+
     private void processDied(ActiveBuffer buffer) {
         synchronized (mLock) {
             mActive.remove(buffer);
-            Log.d("GraphicsStats", "Buffer count: " + mActive.size());
         }
-        HistoricalData data = buffer.mPreviousData;
-        buffer.mPreviousData = null;
-        if (data == null) {
-            data = mHistoricalLog[mNextHistoricalSlot];
-            if (data == null) {
-                data = new HistoricalData();
-            }
-        }
-        data.update(buffer.mPackageName, buffer.mUid, buffer.mProcessBuffer);
-        buffer.closeAllBuffers();
-
-        mHistoricalLog[mNextHistoricalSlot] = data;
-        mNextHistoricalSlot = (mNextHistoricalSlot + 1) % mHistoricalLog.length;
+        addToSaveQueue(buffer);
     }
 
-    private ActiveBuffer fetchActiveBuffersLocked(IBinder token, int uid, int pid,
-            String packageName) throws RemoteException {
+    private ActiveBuffer fetchActiveBuffersLocked(IGraphicsStatsCallback token, int uid, int pid,
+            String packageName, int versionCode) throws RemoteException {
         int size = mActive.size();
+        long today = normalizeDate(System.currentTimeMillis()).getTimeInMillis();
         for (int i = 0; i < size; i++) {
-            ActiveBuffer buffers = mActive.get(i);
-            if (buffers.mPid == pid
-                    && buffers.mUid == uid) {
-                return buffers;
+            ActiveBuffer buffer = mActive.get(i);
+            if (buffer.mPid == pid
+                    && buffer.mUid == uid) {
+                // If the buffer is too old we remove it and return a new one
+                if (buffer.mInfo.startTime < today) {
+                    buffer.binderDied();
+                    break;
+                } else {
+                    return buffer;
+                }
             }
         }
         // Didn't find one, need to create it
         try {
-            ActiveBuffer buffers = new ActiveBuffer(token, uid, pid, packageName);
+            ActiveBuffer buffers = new ActiveBuffer(token, uid, pid, packageName, versionCode);
             mActive.add(buffers);
             return buffers;
         } catch (IOException ex) {
@@ -147,71 +307,106 @@
         }
     }
 
-    private HistoricalData removeHistoricalDataLocked(int uid, String packageName) {
-        for (int i = 0; i < mHistoricalLog.length; i++) {
-            final HistoricalData data = mHistoricalLog[i];
-            if (data != null && data.mUid == uid
-                    && data.mPackageName.equals(packageName)) {
-                if (i == mNextHistoricalSlot) {
-                    mHistoricalLog[i] = null;
-                } else {
-                    mHistoricalLog[i] = mHistoricalLog[mNextHistoricalSlot];
-                    mHistoricalLog[mNextHistoricalSlot] = null;
+    private HashSet<File> dumpActiveLocked(long dump, ArrayList<HistoricalBuffer> buffers) {
+        HashSet<File> skipFiles = new HashSet<>(buffers.size());
+        for (int i = 0; i < buffers.size(); i++) {
+            HistoricalBuffer buffer = buffers.get(i);
+            File path = pathForApp(buffer.mInfo);
+            skipFiles.add(path);
+            nAddToDump(dump, path.getAbsolutePath(), buffer.mInfo.packageName,
+                    buffer.mInfo.versionCode,  buffer.mInfo.startTime, buffer.mInfo.endTime,
+                    buffer.mData);
+        }
+        return skipFiles;
+    }
+
+    private void dumpHistoricalLocked(long dump, HashSet<File> skipFiles) {
+        for (File date : mGraphicsStatsDir.listFiles()) {
+            for (File pkg : date.listFiles()) {
+                for (File version : pkg.listFiles()) {
+                    File data = new File(version, "total");
+                    if (skipFiles.contains(data)) {
+                        continue;
+                    }
+                    nAddToDump(dump, data.getAbsolutePath());
                 }
-                return data;
             }
         }
-        return null;
     }
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+        boolean dumpProto = false;
+        for (String str : args) {
+            if ("--proto".equals(str)) {
+                dumpProto = true;
+                break;
+            }
+        }
+        ArrayList<HistoricalBuffer> buffers;
         synchronized (mLock) {
+            buffers = new ArrayList<>(mActive.size());
             for (int i = 0; i < mActive.size(); i++) {
-                final ActiveBuffer buffer = mActive.get(i);
-                fout.print("Package: ");
-                fout.print(buffer.mPackageName);
-                fout.flush();
                 try {
-                    buffer.mProcessBuffer.readBytes(mTempBuffer, 0, 0, ASHMEM_SIZE);
-                    ThreadedRenderer.dumpProfileData(mTempBuffer, fd);
-                } catch (IOException e) {
-                    fout.println("Failed to dump");
+                    buffers.add(new HistoricalBuffer(mActive.get(i)));
+                } catch (IOException ex) {
+                    // Ignore
                 }
-                fout.println();
             }
-            for (HistoricalData buffer : mHistoricalLog) {
-                if (buffer == null) continue;
-                fout.print("Package: ");
-                fout.print(buffer.mPackageName);
-                fout.flush();
-                ThreadedRenderer.dumpProfileData(buffer.mBuffer, fd);
-                fout.println();
+        }
+        long dump = nCreateDump(fd.getInt$(), dumpProto);
+        try {
+            synchronized (mFileAccessLock) {
+                HashSet<File> skipList = dumpActiveLocked(dump, buffers);
+                buffers.clear();
+                dumpHistoricalLocked(dump, skipList);
             }
+        } finally {
+            nFinishDump(dump);
+        }
+    }
+
+    private static native int nGetAshmemSize();
+    private static native long nCreateDump(int outFd, boolean isProto);
+    private static native void nAddToDump(long dump, String path, String packageName,
+            int versionCode, long startTime, long endTime, byte[] data);
+    private static native void nAddToDump(long dump, String path);
+    private static native void nFinishDump(long dump);
+    private static native void nSaveBuffer(String path, String packageName, int versionCode,
+            long startTime, long endTime, byte[] data);
+
+    private final class BufferInfo {
+        final String packageName;
+        final int versionCode;
+        long startTime;
+        long endTime;
+
+        BufferInfo(String packageName, int versionCode, long startTime) {
+            this.packageName = packageName;
+            this.versionCode = versionCode;
+            this.startTime = startTime;
         }
     }
 
     private final class ActiveBuffer implements DeathRecipient {
+        final BufferInfo mInfo;
         final int mUid;
         final int mPid;
-        final String mPackageName;
+        final IGraphicsStatsCallback mCallback;
         final IBinder mToken;
         MemoryFile mProcessBuffer;
-        HistoricalData mPreviousData;
 
-        ActiveBuffer(IBinder token, int uid, int pid, String packageName)
+        ActiveBuffer(IGraphicsStatsCallback token, int uid, int pid, String packageName, int versionCode)
                 throws RemoteException, IOException {
+            mInfo = new BufferInfo(packageName, versionCode, System.currentTimeMillis());
             mUid = uid;
             mPid = pid;
-            mPackageName = packageName;
-            mToken = token;
+            mCallback = token;
+            mToken = mCallback.asBinder();
             mToken.linkToDeath(this, 0);
-            mProcessBuffer = new MemoryFile("GFXStats-" + uid, ASHMEM_SIZE);
-            mPreviousData = removeHistoricalDataLocked(mUid, mPackageName);
-            if (mPreviousData != null) {
-                mProcessBuffer.writeBytes(mPreviousData.mBuffer, 0, 0, ASHMEM_SIZE);
-            }
+            mProcessBuffer = new MemoryFile("GFXStats-" + pid, ASHMEM_SIZE);
+            mProcessBuffer.writeBytes(ZERO_DATA, 0, 0, ASHMEM_SIZE);
         }
 
         @Override
@@ -228,17 +423,13 @@
         }
     }
 
-    private final static class HistoricalData {
-        final byte[] mBuffer = new byte[ASHMEM_SIZE];
-        int mUid;
-        String mPackageName;
-
-        void update(String packageName, int uid, MemoryFile file) {
-            mUid = uid;
-            mPackageName = packageName;
-            try {
-                file.readBytes(mBuffer, 0, 0, ASHMEM_SIZE);
-            } catch (IOException e) {}
+    private final class HistoricalBuffer {
+        final BufferInfo mInfo;
+        final byte[] mData = new byte[ASHMEM_SIZE];
+        HistoricalBuffer(ActiveBuffer active) throws IOException {
+            mInfo = active.mInfo;
+            mInfo.endTime = System.currentTimeMillis();
+            active.mProcessBuffer.readBytes(mData, 0, 0, ASHMEM_SIZE);
         }
     }
 }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0415971..32136bb 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -70,13 +70,13 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.DiskInfo;
-import android.os.storage.IStorageEventListener;
-import android.os.storage.IStorageShutdownObserver;
 import android.os.storage.IObbActionListener;
+import android.os.storage.IStorageEventListener;
 import android.os.storage.IStorageManager;
-import android.os.storage.StorageManagerInternal;
+import android.os.storage.IStorageShutdownObserver;
 import android.os.storage.OnObbStateChangeListener;
 import android.os.storage.StorageManager;
+import android.os.storage.StorageManagerInternal;
 import android.os.storage.StorageResultCode;
 import android.os.storage.StorageVolume;
 import android.os.storage.VolumeInfo;
@@ -109,6 +109,7 @@
 import com.android.server.NativeDaemonConnector.SensitiveArg;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.storage.AppFuseBridge;
+
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
@@ -3293,14 +3294,69 @@
     }
 
     @Override
-    public long getAllocatableBytes(String path, int flags) {
-        return new File(path).getUsableSpace();
+    public long getAllocatableBytes(String volumeUuid, int flags) {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        final StorageStatsManager stats = mContext.getSystemService(StorageStatsManager.class);
+
+        final boolean aggressive = (flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0;
+        if (aggressive) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.ALLOCATE_AGGRESSIVE, TAG);
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            // In general, apps can allocate as much space as they want, except
+            // we never let them eat into either the minimum cache space or into
+            // the low disk warning space.
+            final File path = storage.findPathForUuid(volumeUuid);
+            if (stats.isQuotaSupported(volumeUuid)) {
+                if (aggressive) {
+                    return Math.max(0,
+                            stats.getFreeBytes(volumeUuid) - storage.getStorageFullBytes(path));
+                } else {
+                    return Math.max(0,
+                            stats.getFreeBytes(volumeUuid) - storage.getStorageLowBytes(path)
+                                    - storage.getStorageCacheBytes(path));
+                }
+            } else {
+                // When we don't have fast quota information, we ignore cached
+                // data and only consider unused bytes.
+                if (aggressive) {
+                    return Math.max(0, path.getUsableSpace() - storage.getStorageFullBytes(path));
+                } else {
+                    return Math.max(0, path.getUsableSpace() - storage.getStorageLowBytes(path));
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
-    public void allocateBytes(String path, long bytes, int flags) {
-        if (bytes > new File(path).getUsableSpace()) {
-            throw new ParcelableException(new IOException("Not enough usable space"));
+    public void allocateBytes(String volumeUuid, long bytes, int flags) {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+
+        // This method call will enforce FLAG_ALLOCATE_AGGRESSIVE permissions so
+        // we don't have to enforce them locally
+        final long allocatableBytes = getAllocatableBytes(volumeUuid, flags);
+        if (bytes > allocatableBytes) {
+            throw new ParcelableException(new IOException("Failed to allocate " + bytes
+                    + " because only " + allocatableBytes + " allocatable"));
+        }
+
+        // Free up enough disk space to satisfy both the requested allocation
+        // and our low disk warning space.
+        final File path = storage.findPathForUuid(volumeUuid);
+        bytes += storage.getStorageLowBytes(path);
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mPms.freeStorage(volumeUuid, bytes, flags);
+        } catch (IOException e) {
+            throw new ParcelableException(e);
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 5b480d0..1b2c75d 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -182,7 +182,8 @@
 
     static {
         ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
-        ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
     }
 
     private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index aaed0e9..8656047 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4235,7 +4235,8 @@
             final int registeredCallbackCount = mUidObservers.getRegisteredCallbackCount();
             for (int i = 0; i < N; ++i) {
                 final UidRecord.ChangeItem item = mActiveUidChanges[i];
-                if (item.change == UidRecord.CHANGE_PROCSTATE) {
+                if (item.change != UidRecord.CHANGE_GONE
+                        && item.change != UidRecord.CHANGE_GONE_IDLE) {
                     mUidStateWithSeqObserver.onUidStateChangedWithSeq(
                             item.uid, item.processState, item.procStateSeq);
                     if (VALIDATE_UID_STATES && registeredCallbackCount == 0) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 3c109ac..1c5233e 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2393,7 +2393,18 @@
         mWindowManager.deferSurfaceLayout();
         try {
             ActivityRecord r = stack.topRunningActivityLocked();
-            stack.resize(pinnedBounds, tempPinnedTaskBounds, null);
+            Rect insetBounds = null;
+            if (tempPinnedTaskBounds != null) {
+                // We always use 0,0 as the position for the inset rect because
+                // if we are getting insets at all in the pinned stack it must mean
+                // we are headed for fullscreen.
+                insetBounds = tempRect;
+                insetBounds.top = 0;
+                insetBounds.left = 0;
+                insetBounds.right = tempPinnedTaskBounds.width();
+                insetBounds.bottom = tempPinnedTaskBounds.height();
+            }
+            stack.resize(pinnedBounds, tempPinnedTaskBounds, insetBounds);
             stack.ensureVisibleActivitiesConfigurationLocked(r, false);
         } finally {
             mWindowManager.continueSurfaceLayout();
@@ -4911,7 +4922,7 @@
     List<IBinder> getTopVisibleActivities() {
         final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
         // Traverse all displays.
-        for (int i = mActivityDisplays.size(); i >= 0; i--) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
             final ActivityDisplay display = mActivityDisplays.valueAt(i);
             // Traverse all stacks on a display.
             for (int j = display.mStacks.size() - 1; j >= 0; j--) {
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 9b459d1..62c3f4a 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -57,7 +57,7 @@
 
     private final ActivityManagerService mActivityManagerService;
 
-    private static final long WAIT_FOR_NETWORK_TIMEOUT_DEFAULT_MS = 2000; // 2 sec
+    private static final long WAIT_FOR_NETWORK_TIMEOUT_DEFAULT_MS = 0; // 0 sec
 
     public CoreSettingsObserver(ActivityManagerService activityManagerService) {
         super(activityManagerService.mHandler);
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 017c5fb..63024db 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -183,11 +183,31 @@
             case CALLBACK_LISTEN_ALL:
                 break;
             case CALLBACK_TRACK_DEFAULT:
+                if (mDefaultNetworkCallback == null) {
+                    // The callback was unregistered in the interval between
+                    // ConnectivityService calling onAvailable() and our
+                    // handling of it here on the mTarget.getHandler() thread.
+                    // Clean-up of this network entry is deferred to the
+                    // handling of onLost() by other callbacks.
+                    // TODO: change to Log.wtf() after oag/331764 is merged.
+                    return;
+                }
+
                 cm().requestNetworkCapabilities(mDefaultNetworkCallback);
                 cm().requestLinkProperties(mDefaultNetworkCallback);
                 mCurrentDefault = network;
                 break;
             case CALLBACK_MOBILE_REQUEST:
+                if (mMobileNetworkCallback == null) {
+                    // The callback was unregistered in the interval between
+                    // ConnectivityService calling onAvailable() and our
+                    // handling of it here on the mTarget.getHandler() thread.
+                    // Clean-up of this network entry is deferred to the
+                    // handling of onLost() by other callbacks.
+                    // TODO: change to Log.wtf() after oag/331764 is merged.
+                    return;
+                }
+
                 cm().requestNetworkCapabilities(mMobileNetworkCallback);
                 cm().requestLinkProperties(mMobileNetworkCallback);
                 break;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 449d808..db04515 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -465,6 +465,15 @@
         }
     }
 
+    public boolean isQuotaSupported(String volumeUuid) throws InstallerException {
+        if (!checkBeforeRemote()) return false;
+        try {
+            return mInstalld.isQuotaSupported(volumeUuid);
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
     private static void assertValidInstructionSet(String instructionSet)
             throws InstallerException {
         for (String abi : Build.SUPPORTED_ABIS) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 463cfac..1c5675a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -24,6 +24,7 @@
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDONLY;
 import static android.system.OsConstants.O_WRONLY;
+
 import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid;
 import static com.android.server.pm.PackageInstallerService.prepareStageDir;
 
@@ -54,8 +55,8 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.SELinux;
 import android.os.UserHandle;
+import android.os.storage.StorageManager;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -66,9 +67,6 @@
 import android.util.MathUtils;
 import android.util.Slog;
 
-import libcore.io.IoUtils;
-import libcore.io.Libcore;
-
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
@@ -78,6 +76,9 @@
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
 
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileFilter;
@@ -455,14 +456,9 @@
 
             // If caller specified a total length, allocate it for them. Free up
             // cache space to grow, if needed.
-            if (lengthBytes > 0) {
-                final StructStat stat = Libcore.os.fstat(targetFd);
-                final long deltaBytes = lengthBytes - stat.st_size;
-                // Only need to free up space when writing to internal stage
-                if (stageDir != null && deltaBytes > 0) {
-                    mPm.freeStorage(params.volumeUuid, deltaBytes);
-                }
-                Libcore.os.posix_fallocate(targetFd, 0, lengthBytes);
+            if (stageDir != null && lengthBytes > 0) {
+                mContext.getSystemService(StorageManager.class).allocateBytes(targetFd,
+                        lengthBytes, 0);
             }
 
             if (offsetBytes > 0) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f95ee8f..371a062 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3821,7 +3821,8 @@
         });
     }
 
-    void freeStorage(String volumeUuid, long freeStorageSize) throws IOException {
+    public void freeStorage(String volumeUuid, long freeStorageSize, int storageFlags)
+            throws IOException {
         synchronized (mInstallLock) {
             try {
                 mInstaller.freeCache(volumeUuid, freeStorageSize, 0);
diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
new file mode 100644
index 0000000..10d30aa
--- /dev/null
+++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2017 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.server.storage;
+
+import android.annotation.MainThread;
+import android.app.usage.CacheQuotaHint;
+import android.app.usage.CacheQuotaService;
+import android.app.usage.ICacheQuotaService;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.format.DateUtils;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.pm.Installer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * CacheQuotaStrategy is a strategy for determining cache quotas using usage stats and foreground
+ * time using the calculation as defined in the refuel rocket.
+ */
+public class CacheQuotaStrategy implements RemoteCallback.OnResultListener {
+    private static final String TAG = "CacheQuotaStrategy";
+
+    private final Object mLock = new Object();
+
+    private final Context mContext;
+    private final UsageStatsManagerInternal mUsageStats;
+    private final Installer mInstaller;
+    private ServiceConnection mServiceConnection;
+    private ICacheQuotaService mRemoteService;
+
+    public CacheQuotaStrategy(
+            Context context, UsageStatsManagerInternal usageStatsManager, Installer installer) {
+        mContext = Preconditions.checkNotNull(context);
+        mUsageStats = Preconditions.checkNotNull(usageStatsManager);
+        mInstaller = Preconditions.checkNotNull(installer);
+    }
+
+    /**
+     * Recalculates the quotas and stores them to installd.
+     */
+    public void recalculateQuotas() {
+        createServiceConnection();
+
+        ComponentName component = getServiceComponentName();
+        if (component != null) {
+            Intent intent = new Intent();
+            intent.setComponent(component);
+            mContext.bindServiceAsUser(
+                    intent, mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.CURRENT);
+        }
+    }
+
+    private void createServiceConnection() {
+        // If we're already connected, don't create a new connection.
+        if (mServiceConnection != null) {
+            return;
+        }
+
+        mServiceConnection = new ServiceConnection() {
+            @Override
+            @MainThread
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                Runnable runnable = new Runnable() {
+                    @Override
+                    public void run() {
+                        synchronized (mLock) {
+                            mRemoteService = ICacheQuotaService.Stub.asInterface(service);
+                            List<CacheQuotaHint> requests = getUnfulfilledRequests();
+                            final RemoteCallback remoteCallback =
+                                    new RemoteCallback(CacheQuotaStrategy.this);
+                            try {
+                                mRemoteService.computeCacheQuotaHints(remoteCallback, requests);
+                            } catch (RemoteException ex) {
+                                Slog.w(TAG,
+                                        "Remote exception occurred while trying to get cache quota",
+                                        ex);
+                            }
+                        }
+                    }
+                };
+                AsyncTask.execute(runnable);
+            }
+
+            @Override
+            @MainThread
+            public void onServiceDisconnected(ComponentName name) {
+                synchronized (mLock) {
+                    mRemoteService = null;
+                }
+            }
+        };
+    }
+
+    /**
+     * Returns a list of CacheQuotaRequests which do not have their quotas filled out for apps
+     * which have been used in the last year.
+     */
+    private List<CacheQuotaHint> getUnfulfilledRequests() {
+        long timeNow = System.currentTimeMillis();
+        long oneYearAgo = timeNow - DateUtils.YEAR_IN_MILLIS;
+
+        List<CacheQuotaHint> requests = new ArrayList<>();
+        UserManager um = mContext.getSystemService(UserManager.class);
+        final List<UserInfo> users = um.getUsers();
+        final int userCount = users.size();
+        final PackageManager packageManager = mContext.getPackageManager();
+        for (int i = 0; i < userCount; i++) {
+            UserInfo info = users.get(i);
+            List<UsageStats> stats =
+                    mUsageStats.queryUsageStatsForUser(info.id, UsageStatsManager.INTERVAL_BEST,
+                            oneYearAgo, timeNow);
+            if (stats == null) {
+                continue;
+            }
+
+            for (UsageStats stat : stats) {
+                String packageName = stat.getPackageName();
+                try {
+                    // We need the app info to determine the uid and the uuid of the volume
+                    // where the app is installed.
+                    ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName, 0);
+                    requests.add(
+                            new CacheQuotaHint.Builder()
+                                    .setVolumeUuid(appInfo.volumeUuid)
+                                    .setUid(appInfo.uid)
+                                    .setUsageStats(stat)
+                                    .setQuota(CacheQuotaHint.QUOTA_NOT_SET)
+                                    .build());
+                } catch (PackageManager.NameNotFoundException e) {
+                    Slog.w(TAG, "Unable to find package for quota calculation", e);
+                    continue;
+                }
+            }
+        }
+        return requests;
+    }
+
+    @Override
+    public void onResult(Bundle data) {
+        final List<CacheQuotaHint> processedRequests =
+                data.getParcelableArrayList(
+                        CacheQuotaService.REQUEST_LIST_KEY);
+        final int requestSize = processedRequests.size();
+        for (int i = 0; i < requestSize; i++) {
+            CacheQuotaHint request = processedRequests.get(i);
+            long proposedQuota = request.getQuota();
+            if (proposedQuota == CacheQuotaHint.QUOTA_NOT_SET) {
+                continue;
+            }
+
+            try {
+                int uid = request.getUid();
+                mInstaller.setAppQuota(request.getVolumeUuid(),
+                        UserHandle.getUserId(uid),
+                        UserHandle.getAppId(uid), proposedQuota);
+            } catch (Installer.InstallerException ex) {
+                Slog.w(TAG,
+                        "Failed to set cache quota for " + request.getUid(),
+                        ex);
+            }
+        }
+
+        disconnectService();
+    }
+
+    private void disconnectService() {
+        mContext.unbindService(mServiceConnection);
+        mServiceConnection = null;
+    }
+
+    private ComponentName getServiceComponentName() {
+        String packageName =
+                mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
+        if (packageName == null) {
+            Slog.w(TAG, "could not access the cache quota service: no package!");
+            return null;
+        }
+
+        Intent intent = new Intent(CacheQuotaService.SERVICE_INTERFACE);
+        intent.setPackage(packageName);
+        ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+            Slog.w(TAG, "No valid components found.");
+            return null;
+        }
+        ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+        return new ComponentName(serviceInfo.packageName, serviceInfo.name);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index cf5cecda..01bd86d 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -176,7 +176,7 @@
             // we trigger any size changes, so it can swap surfaces
             // in to appropriate modes, or do as it wishes otherwise.
             if (!mReplacement) {
-                mTarget.onAnimationStart();
+                mTarget.onAnimationStart(mMoveToFullScreen);
             }
 
             // Immediately update the task bounds if they have to become larger, but preserve
@@ -263,7 +263,7 @@
          */
         boolean setPinnedStackSize(Rect bounds, Rect taskBounds);
 
-        void onAnimationStart();
+        void onAnimationStart(boolean toFullscreen);
 
         /**
          * Callback for the target to inform it that the animation has ended, so it can do some
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ab9a378..da5fcf3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -592,8 +592,15 @@
         return mStack != null && mStack.mStackId == PINNED_STACK_ID;
     }
 
+    /**
+     * When we are in a floating stack (Freeform, Pinned, ...) we calculate
+     * insets differently. However if we are animating to the fullscreen stack
+     * we need to begin calculating insets as if we were fullscreen, otherwise
+     * we will have a jump at the end.
+     */
     boolean isFloating() {
-        return StackId.tasksAreFloating(mStack.mStackId);
+        return StackId.tasksAreFloating(mStack.mStackId)
+            && !mStack.isBoundsAnimatingToFullscreen();
     }
 
     WindowState getTopVisibleAppMainWindow() {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index b9429f4..5f8b694 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -125,6 +125,7 @@
     // perfectly fit the region it would have been cropped to. We may also avoid certain logic we
     // would otherwise apply while resizing, while resizing in the bounds animating mode.
     private boolean mBoundsAnimating = false;
+    private boolean mBoundsAnimatingToFullscreen = false;
     private Rect mBoundsAnimationTarget = new Rect();
 
     // Temporary storage for the new bounds that should be used after the configuration change.
@@ -1443,9 +1444,10 @@
     }
 
     @Override  // AnimatesBounds
-    public void onAnimationStart() {
+    public void onAnimationStart(boolean toFullscreen) {
         synchronized (mService.mWindowMap) {
             mBoundsAnimating = true;
+            mBoundsAnimatingToFullscreen = toFullscreen;
         }
     }
 
@@ -1486,7 +1488,7 @@
         return StackId.hasMovementAnimations(mStackId);
     }
 
-    public boolean getForceScaleToStack() {
+    public boolean isForceScaled() {
         return mBoundsAnimating;
     }
 
@@ -1494,6 +1496,10 @@
         return mBoundsAnimating;
     }
 
+    public boolean isBoundsAnimatingToFullscreen() {
+        return mBoundsAnimating && mBoundsAnimatingToFullscreen;
+    }
+
     /** Returns true if a removal action is still being deferred. */
     boolean checkCompleteDeferredRemoval() {
         if (isAnimating()) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 02c52a7..03ebf19 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2003,7 +2003,7 @@
             if (viewVisibility == View.VISIBLE &&
                     (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                             || !win.mAppToken.clientHidden)) {
-                result = relayoutVisibleWindow(outConfig, result, win, winAnimator, attrChanges,
+                result = win.relayoutVisibleWindow(outConfig, result, attrChanges,
                         oldVisibility);
                 try {
                     result = createSurfaceControl(outSurface, result, win, winAnimator);
@@ -2221,69 +2221,6 @@
         return result;
     }
 
-    private int relayoutVisibleWindow(Configuration outConfig, int result, WindowState win,
-            WindowStateAnimator winAnimator, int attrChanges, int oldVisibility) {
-        result |= !win.isVisibleLw() ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0;
-        if (win.mAnimatingExit) {
-            Slog.d(TAG, "relayoutVisibleWindow: " + win + " mAnimatingExit=true, mRemoveOnExit="
-                    + win.mRemoveOnExit + ", mDestroying=" + win.mDestroying);
-
-            winAnimator.cancelExitAnimationForNextAnimationLocked();
-            win.mAnimatingExit = false;
-        }
-        if (win.mDestroying) {
-            win.mDestroying = false;
-            mDestroySurface.remove(win);
-        }
-        if (oldVisibility == View.GONE) {
-            winAnimator.mEnterAnimationPending = true;
-        }
-
-        win.mLastVisibleLayoutRotation = mRotation;
-
-        winAnimator.mEnteringAnimation = true;
-        if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
-            win.prepareWindowToDisplayDuringRelayout(outConfig);
-        }
-        if ((attrChanges & LayoutParams.FORMAT_CHANGED) != 0) {
-            // If the format can't be changed in place, preserve the old surface until the app draws
-            // on the new one. This prevents blinking when we change elevation of freeform and
-            // pinned windows.
-            if (!winAnimator.tryChangeFormatInPlaceLocked()) {
-                winAnimator.preserveSurfaceLocked();
-                result |= RELAYOUT_RES_SURFACE_CHANGED
-                        | WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
-            }
-        }
-
-        // If we're starting a drag-resize, we'll be changing the surface size as well as
-        // notifying the client to render to with an offset from the surface's top-left.
-        if (win.isDragResizeChanged() || win.isResizedWhileNotDragResizing()) {
-            win.setDragResizing();
-            win.setResizedWhileNotDragResizing(false);
-            // We can only change top level windows to the full-screen surface when
-            // resizing (as we only have one full-screen surface). So there is no need
-            // to preserve and destroy windows which are attached to another, they
-            // will keep their surface and its size may change over time.
-            if (win.mHasSurface && !win.isChildWindow()) {
-                winAnimator.preserveSurfaceLocked();
-                result |= WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
-            }
-        }
-        final boolean freeformResizing = win.isDragResizing()
-                && win.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
-        final boolean dockedResizing = win.isDragResizing()
-                && win.getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-        result |= freeformResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM : 0;
-        result |= dockedResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED : 0;
-        if (win.isAnimatingWithSavedSurface()) {
-            // If we're animating with a saved surface now, request client to report draw.
-            // We still need to know when the real thing is drawn.
-            result |= WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
-        }
-        return result;
-    }
-
     public void performDeferredDestroyWindow(Session session, IWindow client) {
         long origId = Binder.clearCallingIdentity();
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fe7dc5a..945a349 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -40,6 +40,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
 import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.FORMAT_CHANGED;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
@@ -56,6 +57,10 @@
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED;
+import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM;
+import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
+import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static android.view.WindowManagerPolicy.TRANSIT_ENTER;
 import static android.view.WindowManagerPolicy.TRANSIT_EXIT;
@@ -540,6 +545,12 @@
     private static final Region sEmptyRegion = new Region();
 
     /**
+     * Surface insets from the previous call to relayout(), used to track
+     * if we are changing the Surface insets.
+     */
+    final Rect mLastSurfaceInsets = new Rect();
+
+    /**
      * Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
      * of z-order and 1 otherwise.
      */
@@ -1581,7 +1592,7 @@
             // Anyway we don't need to synchronize position and content updates for these
             // windows since they aren't at the base layer and could be moved around anyway.
             if (!computeDragResizing() && mAttrs.type == TYPE_BASE_APPLICATION &&
-                    !getTask().mStack.getBoundsAnimating() && !isGoneForLayoutLw() &&
+                    !mWinAnimator.isForceScaled() && !isGoneForLayoutLw() &&
                     !getTask().inPinnedWorkspace()) {
                 setResizedWhileNotDragResizing(true);
             }
@@ -3878,7 +3889,7 @@
     }
 
     private boolean forAllWindowBottomToTop(ToBooleanFunction<WindowState> callback) {
-        // We want to consumer the negative sublayer children first because they need to appear
+        // We want to consume the negative sublayer children first because they need to appear
         // below the parent, then this window (the parent), and then the positive sublayer children
         // because they need to appear above the parent.
         int i = 0;
@@ -3886,7 +3897,7 @@
         WindowState child = mChildren.get(i);
 
         while (i < count && child.mSubLayer < 0) {
-            if (callback.apply(child)) {
+            if (child.applyInOrderWithImeWindows(callback, false /* traverseTopToBottom */)) {
                 return true;
             }
             i++;
@@ -3901,7 +3912,7 @@
         }
 
         while (i < count) {
-            if (callback.apply(child)) {
+            if (child.applyInOrderWithImeWindows(callback, false /* traverseTopToBottom */)) {
                 return true;
             }
             i++;
@@ -3915,14 +3926,14 @@
     }
 
     private boolean forAllWindowTopToBottom(ToBooleanFunction<WindowState> callback) {
-        // We want to consumer the positive sublayer children first because they need to appear
+        // We want to consume the positive sublayer children first because they need to appear
         // above the parent, then this window (the parent), and then the negative sublayer children
         // because they need to appear above the parent.
         int i = mChildren.size() - 1;
         WindowState child = mChildren.get(i);
 
         while (i >= 0 && child.mSubLayer >= 0) {
-            if (callback.apply(child)) {
+            if (child.applyInOrderWithImeWindows(callback, true /* traverseTopToBottom */)) {
                 return true;
             }
             --i;
@@ -3937,7 +3948,7 @@
         }
 
         while (i >= 0) {
-            if (callback.apply(child)) {
+            if (child.applyInOrderWithImeWindows(callback, true /* traverseTopToBottom */)) {
                 return true;
             }
             --i;
@@ -3980,10 +3991,43 @@
     }
 
     WindowState getWindow(Predicate<WindowState> callback) {
+        if (mChildren.isEmpty()) {
+            return callback.test(this) ? this : null;
+        }
+
+        // We want to consume the positive sublayer children first because they need to appear
+        // above the parent, then this window (the parent), and then the negative sublayer children
+        // because they need to appear above the parent.
+        int i = mChildren.size() - 1;
+        WindowState child = mChildren.get(i);
+
+        while (i >= 0 && child.mSubLayer >= 0) {
+            if (callback.test(child)) {
+                return child;
+            }
+            --i;
+            if (i < 0) {
+                break;
+            }
+            child = mChildren.get(i);
+        }
+
         if (callback.test(this)) {
             return this;
         }
-        return super.getWindow(callback);
+
+        while (i >= 0) {
+            if (callback.test(child)) {
+                return child;
+            }
+            --i;
+            if (i < 0) {
+                break;
+            }
+            child = mChildren.get(i);
+        }
+
+        return null;
     }
 
     boolean isWindowAnimationSet() {
@@ -4298,6 +4342,78 @@
                 -mAttrs.surfaceInsets.bottom);
     }
 
+    boolean surfaceInsetsChanging() {
+        return !mLastSurfaceInsets.equals(mAttrs.surfaceInsets);
+    }
+
+    int relayoutVisibleWindow(Configuration outConfig, int result,
+            int attrChanges, int oldVisibility) {
+        result |= !isVisibleLw() ? RELAYOUT_RES_FIRST_TIME : 0;
+        if (mAnimatingExit) {
+            Slog.d(TAG, "relayoutVisibleWindow: " + this + " mAnimatingExit=true, mRemoveOnExit="
+                    + mRemoveOnExit + ", mDestroying=" + mDestroying);
+
+            mWinAnimator.cancelExitAnimationForNextAnimationLocked();
+            mAnimatingExit = false;
+        }
+        if (mDestroying) {
+            mDestroying = false;
+            mService.mDestroySurface.remove(this);
+        }
+        if (oldVisibility == View.GONE) {
+            mWinAnimator.mEnterAnimationPending = true;
+        }
+
+        mLastVisibleLayoutRotation = mService.mRotation;
+
+        mWinAnimator.mEnteringAnimation = true;
+        if ((result & RELAYOUT_RES_FIRST_TIME) != 0) {
+            prepareWindowToDisplayDuringRelayout(outConfig);
+        }
+        if ((attrChanges & FORMAT_CHANGED) != 0) {
+            // If the format can't be changed in place, preserve the old surface until the app draws
+            // on the new one. This prevents blinking when we change elevation of freeform and
+            // pinned windows.
+            if (!mWinAnimator.tryChangeFormatInPlaceLocked()) {
+                mWinAnimator.preserveSurfaceLocked();
+                result |= RELAYOUT_RES_SURFACE_CHANGED
+                        | RELAYOUT_RES_FIRST_TIME;
+            }
+        }
+
+        // When we change the Surface size, in scenarios which may require changing
+        // the surface position in sync with the resize, we use a preserved surface
+        // so we can freeze it while waiting for the client to report draw on the newly
+        // sized surface.
+        if (isDragResizeChanged() || isResizedWhileNotDragResizing()
+                || surfaceInsetsChanging()) {
+            mLastSurfaceInsets.set(mAttrs.surfaceInsets);
+
+            setDragResizing();
+            setResizedWhileNotDragResizing(false);
+            // We can only change top level windows to the full-screen surface when
+            // resizing (as we only have one full-screen surface). So there is no need
+            // to preserve and destroy windows which are attached to another, they
+            // will keep their surface and its size may change over time.
+            if (mHasSurface && !isChildWindow()) {
+                mWinAnimator.preserveSurfaceLocked();
+                result |= RELAYOUT_RES_FIRST_TIME;
+            }
+        }
+        final boolean freeformResizing = isDragResizing()
+                && getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
+        final boolean dockedResizing = isDragResizing()
+                && getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+        result |= freeformResizing ? RELAYOUT_RES_DRAG_RESIZING_FREEFORM : 0;
+        result |= dockedResizing ? RELAYOUT_RES_DRAG_RESIZING_DOCKED : 0;
+        if (isAnimatingWithSavedSurface()) {
+            // If we're animating with a saved surface now, request client to report draw.
+            // We still need to know when the real thing is drawn.
+            result |= RELAYOUT_RES_FIRST_TIME;
+        }
+        return result;
+    }
+
     // TODO: Hack to work around the number of states AppWindowToken needs to access without having
     // access to its windows children. Need to investigate re-writing
     // {@link AppWindowToken#updateReportedVisibilityLocked} so this can be removed.
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d2ea64c8..c0929cb 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1318,7 +1318,7 @@
         float surfaceWidth = mSurfaceController.getWidth();
         float surfaceHeight = mSurfaceController.getHeight();
 
-        if ((task != null && task.mStack.getForceScaleToStack()) || mForceScaleUntilResize) {
+        if (isForceScaled()) {
             int hInsets = w.getAttrs().surfaceInsets.left + w.getAttrs().surfaceInsets.right;
             int vInsets = w.getAttrs().surfaceInsets.top + w.getAttrs().surfaceInsets.bottom;
             if (!mForceScaleUntilResize) {
@@ -1328,8 +1328,8 @@
             task.mStack.getDimBounds(mTmpStackBounds);
             // We want to calculate the scaling based on the content area, not based on
             // the entire surface, so that we scale in sync with windows that don't have insets.
-            mExtraHScale = (mTmpStackBounds.width() - hInsets) / (float)(surfaceWidth - hInsets);
-            mExtraVScale = (mTmpStackBounds.height() - vInsets) / (float)(surfaceHeight - vInsets);
+            mExtraHScale = mTmpStackBounds.width() / (float)(surfaceWidth - hInsets);
+            mExtraVScale = mTmpStackBounds.height() / (float)(surfaceHeight - vInsets);
 
             // In the case of ForceScaleToStack we scale entire tasks together,
             // and so we need to scale our offsets relative to the task bounds
@@ -1353,6 +1353,7 @@
             // expose the whole window in buffer space, and not risk extending
             // past where the system would have cropped us
             clipRect = null;
+            finalClipRect = null;
 
             // Various surfaces in the scaled stack may resize at different times.
             // We need to ensure for each surface, that we disable transformation matrix
@@ -1952,4 +1953,16 @@
             }
         }
     }
+
+    /** The force-scaled state for a given window can persist past
+     * the state for it's stack as the windows complete resizing
+     * independently of one another.
+     */
+    boolean isForceScaled() {
+        final Task task = mWin.getTask();
+        if (task != null && task.mStack.isForceScaled()) {
+            return true;
+        }
+        return mForceScaleUntilResize;
+    }
 }
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 2c3cda5..ac95db5 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -30,6 +30,7 @@
     $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
     $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_PersistentDataBlockService.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_GraphicsStatsService.cpp \
     $(LOCAL_REL_DIR)/onload.cpp
 
 LOCAL_C_INCLUDES += \
@@ -37,7 +38,6 @@
     external/scrypt/lib/crypto \
     frameworks/base/services \
     frameworks/base/libs \
-    frameworks/base/libs/hwui \
     frameworks/base/core/jni \
     frameworks/native/services \
     system/core/libappfuse/include \
@@ -76,6 +76,7 @@
     libhidltransport \
     libhwbinder \
     libutils \
+    libhwui \
     android.hardware.audio.common@2.0 \
     android.hardware.contexthub@1.0 \
     android.hardware.gnss@1.0 \
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
new file mode 100644
index 0000000..5d5728d
--- /dev/null
+++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "GraphicsStatsService"
+
+#include <jni.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <ScopedPrimitiveArray.h>
+#include <ScopedUtfChars.h>
+#include <JankTracker.h>
+#include <service/GraphicsStatsService.h>
+
+namespace android {
+
+using namespace android::uirenderer;
+
+static jint getAshmemSize(JNIEnv*, jobject) {
+    return sizeof(ProfileData);
+}
+
+static jlong createDump(JNIEnv*, jobject, jint fd, jboolean isProto) {
+    GraphicsStatsService::Dump* dump = GraphicsStatsService::createDump(fd, isProto
+            ? GraphicsStatsService::DumpType::Protobuf : GraphicsStatsService::DumpType::Text);
+    return reinterpret_cast<jlong>(dump);
+}
+
+static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage,
+        jint versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
+    std::string path;
+    const ProfileData* data = nullptr;
+    LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
+    if (jdata != nullptr) {
+        ScopedByteArrayRO buffer(env, jdata);
+        LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
+                "Buffer size %zu doesn't match expected %zu!", buffer.size(), sizeof(ProfileData));
+        data = reinterpret_cast<const ProfileData*>(buffer.get());
+    }
+    if (jpath != nullptr) {
+        ScopedUtfChars pathChars(env, jpath);
+        LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
+        path.assign(pathChars.c_str(), pathChars.size());
+    }
+    ScopedUtfChars packageChars(env, jpackage);
+    LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(), "Failed to get path chars");
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer");
+
+    const std::string package(packageChars.c_str(), packageChars.size());
+    GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data);
+}
+
+static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) {
+    ScopedUtfChars pathChars(env, jpath);
+    LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
+    const std::string path(pathChars.c_str(), pathChars.size());
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    GraphicsStatsService::addToDump(dump, path);
+}
+
+static void finishDump(JNIEnv*, jobject, jlong dumpPtr) {
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    GraphicsStatsService::finishDump(dump);
+}
+
+static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
+        jint versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
+    ScopedByteArrayRO buffer(env, jdata);
+    LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
+            "Buffer size %zu doesn't match expected %zu!", buffer.size(), sizeof(ProfileData));
+    ScopedUtfChars pathChars(env, jpath);
+    LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
+    ScopedUtfChars packageChars(env, jpackage);
+    LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(), "Failed to get path chars");
+
+    const std::string path(pathChars.c_str(), pathChars.size());
+    const std::string package(packageChars.c_str(), packageChars.size());
+    const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer.get());
+    GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data);
+}
+
+static const JNINativeMethod sMethods[] = {
+    { "nGetAshmemSize", "()I", (void*) getAshmemSize },
+    { "nCreateDump", "(IZ)J", (void*) createDump },
+    { "nAddToDump", "(JLjava/lang/String;Ljava/lang/String;IJJ[B)V", (void*) addToDump },
+    { "nAddToDump", "(JLjava/lang/String;)V", (void*) addFileToDump },
+    { "nFinishDump", "(J)V", (void*) finishDump },
+    { "nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;IJJ[B)V", (void*) saveBuffer },
+};
+
+int register_android_server_GraphicsStatsService(JNIEnv* env)
+{
+    return jniRegisterNativeMethods(env, "com/android/server/GraphicsStatsService",
+                                    sMethods, NELEM(sMethods));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 899640e..f22b330 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -46,6 +46,7 @@
 int register_android_server_Watchdog(JNIEnv* env);
 int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
 int register_android_server_SyntheticPasswordManager(JNIEnv* env);
+int register_android_server_GraphicsStatsService(JNIEnv* env);
 };
 
 using namespace android;
@@ -87,6 +88,7 @@
     register_android_server_HardwarePropertiesManagerService(env);
     register_android_server_storage_AppFuse(env);
     register_android_server_SyntheticPasswordManager(env);
+    register_android_server_GraphicsStatsService(env);
 
     return JNI_VERSION_1_4;
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 30f99e5..bd3271b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -16,24 +16,25 @@
 
 package com.android.server.wm;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import android.content.res.Configuration;
-import android.hardware.display.DisplayManagerGlobal;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.Display;
-import android.view.DisplayInfo;
 
-import java.util.ArrayList;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
-import static org.junit.Assert.assertEquals;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
 
 /**
  * Tests for the {@link DisplayContent} class.
@@ -54,38 +55,17 @@
         exitingAppToken.mIsExiting = true;
         exitingAppToken.mTask.mStack.mExitingAppTokens.add(exitingAppToken);
 
-        final ArrayList<WindowState> windows = new ArrayList();
-
-        // Test forward traversal.
-        sDisplayContent.forAllWindows(w -> {windows.add(w);}, false /* traverseTopToBottom */);
-
-        assertEquals(sWallpaperWindow, windows.get(0));
-        assertEquals(exitingAppWindow, windows.get(1));
-        assertEquals(sChildAppWindowBelow, windows.get(2));
-        assertEquals(sAppWindow, windows.get(3));
-        assertEquals(sChildAppWindowAbove, windows.get(4));
-        assertEquals(sDockedDividerWindow, windows.get(5));
-        assertEquals(sStatusBarWindow, windows.get(6));
-        assertEquals(sNavBarWindow, windows.get(7));
-        assertEquals(sImeWindow, windows.get(8));
-        assertEquals(sImeDialogWindow, windows.get(9));
-
-        // Test backward traversal.
-        windows.clear();
-        sDisplayContent.forAllWindows(w -> {windows.add(w);}, true /* traverseTopToBottom */);
-
-        assertEquals(sWallpaperWindow, windows.get(9));
-        assertEquals(exitingAppWindow, windows.get(8));
-        assertEquals(sChildAppWindowBelow, windows.get(7));
-        assertEquals(sAppWindow, windows.get(6));
-        assertEquals(sChildAppWindowAbove, windows.get(5));
-        assertEquals(sDockedDividerWindow, windows.get(4));
-        assertEquals(sStatusBarWindow, windows.get(3));
-        assertEquals(sNavBarWindow, windows.get(2));
-        assertEquals(sImeWindow, windows.get(1));
-        assertEquals(sImeDialogWindow, windows.get(0));
-
-        exitingAppWindow.removeImmediately();
+        assertForAllWindowsOrder(Arrays.asList(
+                sWallpaperWindow,
+                exitingAppWindow,
+                sChildAppWindowBelow,
+                sAppWindow,
+                sChildAppWindowAbove,
+                sDockedDividerWindow,
+                sStatusBarWindow,
+                sNavBarWindow,
+                sImeWindow,
+                sImeDialogWindow));
     }
 
     @Test
@@ -95,78 +75,49 @@
 
         sWm.mInputMethodTarget = imeAppTarget;
 
-        final ArrayList<WindowState> windows = new ArrayList();
+        assertForAllWindowsOrder(Arrays.asList(
+                sWallpaperWindow,
+                sChildAppWindowBelow,
+                sAppWindow,
+                sChildAppWindowAbove,
+                imeAppTarget,
+                sImeWindow,
+                sImeDialogWindow,
+                sDockedDividerWindow,
+                sStatusBarWindow,
+                sNavBarWindow));
+    }
 
-        // Test forward traversal.
-        sDisplayContent.forAllWindows(w -> {windows.add(w);}, false /* traverseTopToBottom */);
+    @Test
+    public void testForAllWindows_WithChildWindowImeTarget() throws Exception {
+        sWm.mInputMethodTarget = sChildAppWindowAbove;
 
-        assertEquals(sWallpaperWindow, windows.get(0));
-        assertEquals(sChildAppWindowBelow, windows.get(1));
-        assertEquals(sAppWindow, windows.get(2));
-        assertEquals(sChildAppWindowAbove, windows.get(3));
-        assertEquals(imeAppTarget, windows.get(4));
-        assertEquals(sImeWindow, windows.get(5));
-        assertEquals(sImeDialogWindow, windows.get(6));
-        assertEquals(sDockedDividerWindow, windows.get(7));
-        assertEquals(sStatusBarWindow, windows.get(8));
-        assertEquals(sNavBarWindow, windows.get(9));
-
-        // Test backward traversal.
-        windows.clear();
-        sDisplayContent.forAllWindows(w -> {windows.add(w);}, true /* traverseTopToBottom */);
-
-        assertEquals(sWallpaperWindow, windows.get(9));
-        assertEquals(sChildAppWindowBelow, windows.get(8));
-        assertEquals(sAppWindow, windows.get(7));
-        assertEquals(sChildAppWindowAbove, windows.get(6));
-        assertEquals(imeAppTarget, windows.get(5));
-        assertEquals(sImeWindow, windows.get(4));
-        assertEquals(sImeDialogWindow, windows.get(3));
-        assertEquals(sDockedDividerWindow, windows.get(2));
-        assertEquals(sStatusBarWindow, windows.get(1));
-        assertEquals(sNavBarWindow, windows.get(0));
-
-        // Clean-up
-        sWm.mInputMethodTarget = null;
-        imeAppTarget.removeImmediately();
+        assertForAllWindowsOrder(Arrays.asList(
+                sWallpaperWindow,
+                sChildAppWindowBelow,
+                sAppWindow,
+                sChildAppWindowAbove,
+                sImeWindow,
+                sImeDialogWindow,
+                sDockedDividerWindow,
+                sStatusBarWindow,
+                sNavBarWindow));
     }
 
     @Test
     public void testForAllWindows_WithStatusBarImeTarget() throws Exception {
-
         sWm.mInputMethodTarget = sStatusBarWindow;
 
-        final ArrayList<WindowState> windows = new ArrayList();
-
-        // Test forward traversal.
-        sDisplayContent.forAllWindows(w -> {windows.add(w);}, false /* traverseTopToBottom */);
-
-        assertEquals(sWallpaperWindow, windows.get(0));
-        assertEquals(sChildAppWindowBelow, windows.get(1));
-        assertEquals(sAppWindow, windows.get(2));
-        assertEquals(sChildAppWindowAbove, windows.get(3));
-        assertEquals(sDockedDividerWindow, windows.get(4));
-        assertEquals(sStatusBarWindow, windows.get(5));
-        assertEquals(sImeWindow, windows.get(6));
-        assertEquals(sImeDialogWindow, windows.get(7));
-        assertEquals(sNavBarWindow, windows.get(8));
-
-        // Test backward traversal.
-        windows.clear();
-        sDisplayContent.forAllWindows(w -> {windows.add(w);}, true /* traverseTopToBottom */);
-
-        assertEquals(sWallpaperWindow, windows.get(8));
-        assertEquals(sChildAppWindowBelow, windows.get(7));
-        assertEquals(sAppWindow, windows.get(6));
-        assertEquals(sChildAppWindowAbove, windows.get(5));
-        assertEquals(sDockedDividerWindow, windows.get(4));
-        assertEquals(sStatusBarWindow, windows.get(3));
-        assertEquals(sImeWindow, windows.get(2));
-        assertEquals(sImeDialogWindow, windows.get(1));
-        assertEquals(sNavBarWindow, windows.get(0));
-
-        // Clean-up
-        sWm.mInputMethodTarget = null;
+        assertForAllWindowsOrder(Arrays.asList(
+                sWallpaperWindow,
+                sChildAppWindowBelow,
+                sAppWindow,
+                sChildAppWindowAbove,
+                sDockedDividerWindow,
+                sStatusBarWindow,
+                sImeWindow,
+                sImeDialogWindow,
+                sNavBarWindow));
     }
 
     @Test
@@ -176,38 +127,35 @@
         final WindowState voiceInteractionWindow = createWindow(null, TYPE_VOICE_INTERACTION,
                 sDisplayContent, "voiceInteractionWindow");
 
-        final ArrayList<WindowState> windows = new ArrayList();
+        assertForAllWindowsOrder(Arrays.asList(
+                sWallpaperWindow,
+                sChildAppWindowBelow,
+                sAppWindow,
+                sChildAppWindowAbove,
+                sDockedDividerWindow,
+                voiceInteractionWindow,
+                sStatusBarWindow,
+                sNavBarWindow,
+                sImeWindow,
+                sImeDialogWindow));
+    }
 
-        // Test forward traversal.
-        sDisplayContent.forAllWindows(w -> {windows.add(w);}, false /* traverseTopToBottom */);
+    @Test
+    public void testComputeImeTarget() throws Exception {
+        // Verify that an app window can be an ime target.
+        final WindowState appWin = createWindow(null, TYPE_APPLICATION, sDisplayContent, "appWin");
+        appWin.setHasSurface(true);
+        assertTrue(appWin.canBeImeTarget());
+        WindowState imeTarget = sDisplayContent.computeImeTarget(false /* updateImeTarget */);
+        assertEquals(appWin, imeTarget);
 
-        assertEquals(sWallpaperWindow, windows.get(0));
-        assertEquals(sChildAppWindowBelow, windows.get(1));
-        assertEquals(sAppWindow, windows.get(2));
-        assertEquals(sChildAppWindowAbove, windows.get(3));
-        assertEquals(sDockedDividerWindow, windows.get(4));
-        assertEquals(voiceInteractionWindow, windows.get(5));
-        assertEquals(sStatusBarWindow, windows.get(6));
-        assertEquals(sNavBarWindow, windows.get(7));
-        assertEquals(sImeWindow, windows.get(8));
-        assertEquals(sImeDialogWindow, windows.get(9));
-
-        // Test backward traversal.
-        windows.clear();
-        sDisplayContent.forAllWindows(w -> {windows.add(w);}, true /* traverseTopToBottom */);
-
-        assertEquals(sWallpaperWindow, windows.get(9));
-        assertEquals(sChildAppWindowBelow, windows.get(8));
-        assertEquals(sAppWindow, windows.get(7));
-        assertEquals(sChildAppWindowAbove, windows.get(6));
-        assertEquals(sDockedDividerWindow, windows.get(5));
-        assertEquals(voiceInteractionWindow, windows.get(4));
-        assertEquals(sStatusBarWindow, windows.get(3));
-        assertEquals(sNavBarWindow, windows.get(2));
-        assertEquals(sImeWindow, windows.get(1));
-        assertEquals(sImeDialogWindow, windows.get(0));
-
-        voiceInteractionWindow.removeImmediately();
+        // Verify that an child window can be an ime target.
+        final WindowState childWin = createWindow(appWin,
+                TYPE_APPLICATION_ATTACHED_DIALOG, "childWin");
+        childWin.setHasSurface(true);
+        assertTrue(childWin.canBeImeTarget());
+        imeTarget = sDisplayContent.computeImeTarget(false /* updateImeTarget */);
+        assertEquals(childWin, imeTarget);
     }
 
     /**
@@ -284,4 +232,24 @@
         assertEquals(currentOverrideConfig.densityDpi, globalConfig.densityDpi);
         assertEquals(currentOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
     }
+
+    private void assertForAllWindowsOrder(List<WindowState> expectedWindows) {
+        final LinkedList<WindowState> actualWindows = new LinkedList();
+
+        // Test forward traversal.
+        sDisplayContent.forAllWindows(actualWindows::addLast, false /* traverseTopToBottom */);
+        assertEquals(expectedWindows.size(), actualWindows.size());
+        for (WindowState w : expectedWindows) {
+            assertEquals(w, actualWindows.pollFirst());
+        }
+        assertTrue(actualWindows.isEmpty());
+
+        // Test backward traversal.
+        sDisplayContent.forAllWindows(actualWindows::addLast, true /* traverseTopToBottom */);
+        assertEquals(expectedWindows.size(), actualWindows.size());
+        for (WindowState w : expectedWindows) {
+            assertEquals(w, actualWindows.pollLast());
+        }
+        assertTrue(actualWindows.isEmpty());
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index df35b7ee..5f51898 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -23,10 +23,17 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import java.util.LinkedList;
+
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -167,4 +174,34 @@
         assertFalse(appWindow.canBeImeTarget());
         assertFalse(imeWindow.canBeImeTarget());
     }
+
+    @Test
+    public void testGetWindow() throws Exception {
+        final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
+        final WindowState mediaChild = createWindow(root, TYPE_APPLICATION_MEDIA, "mediaChild");
+        final WindowState mediaOverlayChild = createWindow(root,
+                TYPE_APPLICATION_MEDIA_OVERLAY, "mediaOverlayChild");
+        final WindowState attachedDialogChild = createWindow(root,
+                TYPE_APPLICATION_ATTACHED_DIALOG, "attachedDialogChild");
+        final WindowState subPanelChild = createWindow(root,
+                TYPE_APPLICATION_SUB_PANEL, "subPanelChild");
+        final WindowState aboveSubPanelChild = createWindow(root,
+                TYPE_APPLICATION_ABOVE_SUB_PANEL, "aboveSubPanelChild");
+
+        final LinkedList<WindowState> windows = new LinkedList();
+
+        root.getWindow(w -> {
+            windows.addLast(w);
+            return false;
+        });
+
+        // getWindow should have returned candidate windows in z-order.
+        assertEquals(aboveSubPanelChild, windows.pollFirst());
+        assertEquals(subPanelChild, windows.pollFirst());
+        assertEquals(attachedDialogChild, windows.pollFirst());
+        assertEquals(root, windows.pollFirst());
+        assertEquals(mediaOverlayChild, windows.pollFirst());
+        assertEquals(mediaChild, windows.pollFirst());
+        assertTrue(windows.isEmpty());
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index e5e3512..52e10a5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -44,6 +44,7 @@
 import static android.content.res.Configuration.EMPTY;
 import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
@@ -60,6 +61,7 @@
 import com.android.server.AttributeCache;
 
 import java.util.HashSet;
+import java.util.LinkedList;
 
 /**
  * Common base class for window manager unit test classes.
@@ -120,6 +122,7 @@
         sCommonWindows = new HashSet();
         sWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow");
         sImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "sImeWindow");
+        sWm.mInputMethodWindow = sImeWindow;
         sImeDialogWindow = createCommonWindow(null, TYPE_INPUT_METHOD_DIALOG, "sImeDialogWindow");
         sStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "sStatusBarWindow");
         sNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "sNavBarWindow");
@@ -133,16 +136,25 @@
 
     @After
     public void tearDown() throws Exception {
+        final LinkedList<WindowState> nonCommonWindows = new LinkedList();
         sWm.mRoot.forAllWindows(w -> {
             if (!sCommonWindows.contains(w)) {
-                w.removeImmediately();
+                nonCommonWindows.addLast(w);
             }
         }, true /* traverseTopToBottom */);
+
+        while (!nonCommonWindows.isEmpty()) {
+            nonCommonWindows.pollLast().removeImmediately();
+        }
+
+        sWm.mInputMethodTarget = null;
     }
 
     private static WindowState createCommonWindow(WindowState parent, int type, String name) {
         final WindowState win = createWindow(parent, type, name);
         sCommonWindows.add(win);
+        // Prevent common windows from been IMe targets
+        win.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
         return win;
     }
 
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 6826975..a44860e 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -20,6 +20,7 @@
 import android.app.usage.ExternalStorageStats;
 import android.app.usage.IStorageStatsManager;
 import android.app.usage.StorageStats;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -28,25 +29,35 @@
 import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.StatFs;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageEventListener;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
+import android.text.format.DateUtils;
 import android.util.Slog;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
+import com.android.server.IoThread;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
+import com.android.server.storage.CacheQuotaStrategy;
 
 public class StorageStatsService extends IStorageStatsManager.Stub {
     private static final String TAG = "StorageStatsService";
 
     private static final String PROP_VERIFY_STORAGE = "fw.verify_storage";
 
+    private static final long DELAY_IN_MILLIS = 30 * DateUtils.SECOND_IN_MILLIS;
+
     public static class Lifecycle extends SystemService {
         private StorageStatsService mService;
 
@@ -68,6 +79,7 @@
     private final StorageManager mStorage;
 
     private final Installer mInstaller;
+    private final H mHandler;
 
     public StorageStatsService(Context context) {
         mContext = Preconditions.checkNotNull(context);
@@ -80,6 +92,9 @@
         mInstaller.onStart();
         invalidateMounts();
 
+        mHandler = new H(IoThread.get().getLooper());
+        mHandler.sendEmptyMessageDelayed(H.MSG_CHECK_STORAGE_DELTA, DELAY_IN_MILLIS);
+
         mStorage.registerListener(new StorageEventListener() {
             @Override
             public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
@@ -115,6 +130,17 @@
     }
 
     @Override
+    public boolean isQuotaSupported(String volumeUuid, String callingPackage) {
+        enforcePermission(Binder.getCallingUid(), callingPackage);
+
+        try {
+            return mInstaller.isQuotaSupported(volumeUuid);
+        } catch (InstallerException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    @Override
     public long getTotalBytes(String volumeUuid, String callingPackage) {
         enforcePermission(Binder.getCallingUid(), callingPackage);
 
@@ -274,4 +300,63 @@
         res.cacheBytes = stats.cacheSize + stats.externalCacheSize;
         return res;
     }
+
+    private class H extends Handler {
+        private static final int MSG_CHECK_STORAGE_DELTA = 100;
+        /**
+         * By only triggering a re-calculation after the storage has changed sizes, we can avoid
+         * recalculating quotas too often. Minimum change delta defines the percentage of change
+         * we need to see before we recalculate.
+         */
+        private static final double MINIMUM_CHANGE_DELTA = 0.05;
+        private static final boolean DEBUG = false;
+
+        private final StatFs mStats;
+        private long mPreviousBytes;
+        private double mMinimumThresholdBytes;
+
+        public H(Looper looper) {
+            super(looper);
+            // TODO: Handle all private volumes.
+            mStats = new StatFs(Environment.getDataDirectory().getAbsolutePath());
+            mPreviousBytes = mStats.getFreeBytes();
+            mMinimumThresholdBytes = mStats.getTotalBytes() * MINIMUM_CHANGE_DELTA;
+            // TODO: Load cache quotas from a file to avoid re-doing work.
+        }
+
+        public void handleMessage(Message msg) {
+            if (DEBUG) {
+                Slog.v(TAG, ">>> handling " + msg.what);
+            }
+            switch (msg.what) {
+                case MSG_CHECK_STORAGE_DELTA: {
+                    long bytesDelta = Math.abs(mPreviousBytes - mStats.getFreeBytes());
+                    if (bytesDelta > mMinimumThresholdBytes) {
+                        mPreviousBytes = mStats.getFreeBytes();
+                        recalculateQuotas();
+                    }
+                    sendEmptyMessageDelayed(MSG_CHECK_STORAGE_DELTA, DELAY_IN_MILLIS);
+                    break;
+                }
+                default:
+                    if (DEBUG) {
+                        Slog.v(TAG, ">>> default message case ");
+                    }
+                    return;
+            }
+        }
+
+        private void recalculateQuotas() {
+            if (DEBUG) {
+                Slog.v(TAG, ">>> recalculating quotas ");
+            }
+
+            UsageStatsManagerInternal usageStatsManager =
+                    LocalServices.getService(UsageStatsManagerInternal.class);
+            CacheQuotaStrategy strategy = new CacheQuotaStrategy(
+                    mContext, usageStatsManager, mInstaller);
+            // TODO: Save cache quotas to an XML file.
+            strategy.recalculateQuotas();
+        }
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 7a69803..3c743b5 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1603,5 +1603,12 @@
                 userStats.applyRestoredPayload(key, payload);
             }
         }
+
+        @Override
+        public List<UsageStats> queryUsageStatsForUser(
+                int userId, int intervalType, long beginTime, long endTime) {
+            return UsageStatsService.this.queryUsageStats(
+                    userId, intervalType, beginTime, endTime);
+        }
     }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c2c724f..7c9c0e8 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5522,6 +5522,44 @@
     }
 
     /**
+     * Set SIM card power state. Request is equivalent to inserting or removing the card.
+     *
+     * @param powerUp True if powering up the SIM, otherwise powering down
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     * @hide
+     **/
+    public void setSimPowerState(boolean powerUp) {
+        setSimPowerStateForSlot(getDefaultSim(), powerUp);
+    }
+
+    /**
+     * Set SIM card power state. Request is equivalent to inserting or removing the card.
+     *
+     * @param slotId SIM slot id
+     * @param powerUp True if powering up the SIM, otherwise powering down
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     * @hide
+     **/
+    public void setSimPowerStateForSlot(int slotId, boolean powerUp) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setSimPowerStateForSlot(slotId, powerUp);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Permission error calling ITelephony#setSimPowerStateForSlot", e);
+        }
+    }
+
+    /**
      * Set baseband version for the default phone.
      *
      * @param version baseband version
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 0f865a8..fe8dbfb 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -128,23 +128,23 @@
         }
 
         @Override
-        public boolean isConnected(int slotId, int featureType, int sessionId, int callSessionType,
-                int callType) throws RemoteException {
+        public boolean isConnected(int slotId, int featureType, int callSessionType, int callType)
+                throws RemoteException {
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
-                    return feature.isConnected(sessionId, callSessionType, callType);
+                    return feature.isConnected(callSessionType, callType);
                 }
             }
             return false;
         }
 
         @Override
-        public boolean isOpened(int slotId, int featureType, int sessionId) throws RemoteException {
+        public boolean isOpened(int slotId, int featureType) throws RemoteException {
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
-                    return feature.isOpened(sessionId);
+                    return feature.isOpened();
                 }
             }
             return false;
@@ -166,23 +166,23 @@
         }
 
         @Override
-        public void addRegistrationListener(int slotId, int featureType, int sessionId,
+        public void addRegistrationListener(int slotId, int featureType,
                 IImsRegistrationListener listener) throws RemoteException {
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
-                    feature.addRegistrationListener(sessionId, listener);
+                    feature.addRegistrationListener(listener);
                 }
             }
         }
 
         @Override
-        public void removeRegistrationListener(int slotId, int featureType, int sessionId,
+        public void removeRegistrationListener(int slotId, int featureType,
                 IImsRegistrationListener listener) throws RemoteException {
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
-                    feature.removeRegistrationListener(sessionId, listener);
+                    feature.removeRegistrationListener(listener);
                 }
             }
         }
@@ -224,79 +224,79 @@
         }
 
         @Override
-        public IImsUt getUtInterface(int slotId, int featureType, int sessionId)
+        public IImsUt getUtInterface(int slotId, int featureType)
                 throws RemoteException {
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
-                    return feature.getUtInterface(sessionId);
+                    return feature.getUtInterface();
                 }
             }
             return null;
         }
 
         @Override
-        public IImsConfig getConfigInterface(int slotId, int featureType, int sessionId)
+        public IImsConfig getConfigInterface(int slotId, int featureType)
                 throws RemoteException {
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
-                    return feature.getConfigInterface(sessionId);
+                    return feature.getConfigInterface();
                 }
             }
             return null;
         }
 
         @Override
-        public void turnOnIms(int slotId, int featureType, int sessionId) throws RemoteException {
+        public void turnOnIms(int slotId, int featureType) throws RemoteException {
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
-                    feature.turnOnIms(sessionId);
+                    feature.turnOnIms();
                 }
             }
         }
 
         @Override
-        public void turnOffIms(int slotId, int featureType, int sessionId) throws RemoteException {
+        public void turnOffIms(int slotId, int featureType) throws RemoteException {
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
-                    feature.turnOffIms(sessionId);
+                    feature.turnOffIms();
                 }
             }
         }
 
         @Override
-        public IImsEcbm getEcbmInterface(int slotId, int featureType, int sessionId)
+        public IImsEcbm getEcbmInterface(int slotId, int featureType)
                 throws RemoteException {
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
-                    return feature.getEcbmInterface(sessionId);
+                    return feature.getEcbmInterface();
                 }
             }
             return null;
         }
 
         @Override
-        public void setUiTTYMode(int slotId, int featureType, int sessionId, int uiTtyMode,
-                Message onComplete) throws RemoteException {
+        public void setUiTTYMode(int slotId, int featureType, int uiTtyMode, Message onComplete)
+                throws RemoteException {
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
-                    feature.setUiTTYMode(sessionId, uiTtyMode, onComplete);
+                    feature.setUiTTYMode(uiTtyMode, onComplete);
                 }
             }
         }
 
         @Override
-        public IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType,
-                int sessionId) throws RemoteException {
+        public IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType)
+                throws RemoteException {
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
-                    return feature.getMultiEndpointInterface(sessionId);
+                    return feature.getMultiEndpointInterface();
                 }
             }
             return null;
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxy.java b/telephony/java/android/telephony/ims/ImsServiceProxy.java
index b2cdba2..38ea6e6f 100644
--- a/telephony/java/android/telephony/ims/ImsServiceProxy.java
+++ b/telephony/java/android/telephony/ims/ImsServiceProxy.java
@@ -135,40 +135,40 @@
     }
 
     @Override
-    public boolean isConnected(int sessionId, int callServiceType, int callType)
+    public boolean isConnected(int callServiceType, int callType)
             throws RemoteException {
         synchronized (mLock) {
             checkBinderConnection();
-            return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature, sessionId,
+            return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature,
                     callServiceType, callType);
         }
     }
 
     @Override
-    public boolean isOpened(int sessionId) throws RemoteException {
+    public boolean isOpened() throws RemoteException {
         synchronized (mLock) {
             checkBinderConnection();
-            return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature, sessionId);
+            return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature);
         }
     }
 
     @Override
-    public void addRegistrationListener(int sessionId, IImsRegistrationListener listener)
+    public void addRegistrationListener(IImsRegistrationListener listener)
     throws RemoteException {
         synchronized (mLock) {
             checkBinderConnection();
             getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature,
-                    sessionId, listener);
+                    listener);
         }
     }
 
     @Override
-    public void removeRegistrationListener(int sessionId, IImsRegistrationListener listener)
+    public void removeRegistrationListener(IImsRegistrationListener listener)
             throws RemoteException {
         synchronized (mLock) {
             checkBinderConnection();
             getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature,
-                    sessionId, listener);
+                    listener);
         }
     }
 
@@ -203,64 +203,61 @@
     }
 
     @Override
-    public IImsUt getUtInterface(int sessionId) throws RemoteException {
+    public IImsUt getUtInterface() throws RemoteException {
         synchronized (mLock) {
             checkBinderConnection();
-            return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature,
-                    sessionId);
+            return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature);
         }
     }
 
     @Override
-    public IImsConfig getConfigInterface(int sessionId) throws RemoteException {
+    public IImsConfig getConfigInterface() throws RemoteException {
         synchronized (mLock) {
             checkBinderConnection();
-            return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature,
-                    sessionId);
+            return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature);
         }
     }
 
     @Override
-    public void turnOnIms(int sessionId) throws RemoteException {
+    public void turnOnIms() throws RemoteException {
         synchronized (mLock) {
             checkBinderConnection();
-            getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature, sessionId);
+            getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature);
         }
     }
 
     @Override
-    public void turnOffIms(int sessionId) throws RemoteException {
+    public void turnOffIms() throws RemoteException {
         synchronized (mLock) {
             checkBinderConnection();
-            getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature, sessionId);
+            getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature);
         }
     }
 
     @Override
-    public IImsEcbm getEcbmInterface(int sessionId) throws RemoteException {
+    public IImsEcbm getEcbmInterface() throws RemoteException {
         synchronized (mLock) {
             checkBinderConnection();
-            return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature,
-                    sessionId);
+            return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature);
         }
     }
 
     @Override
-    public void setUiTTYMode(int sessionId, int uiTtyMode, Message onComplete)
+    public void setUiTTYMode(int uiTtyMode, Message onComplete)
             throws RemoteException {
         synchronized (mLock) {
             checkBinderConnection();
-            getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, sessionId,
-                    uiTtyMode, onComplete);
+            getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, uiTtyMode,
+                    onComplete);
         }
     }
 
     @Override
-    public IImsMultiEndpoint getMultiEndpointInterface(int sessionId) throws RemoteException {
+    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
         synchronized (mLock) {
             checkBinderConnection();
             return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId,
-                    mSupportedFeature, sessionId);
+                    mSupportedFeature);
         }
     }
 
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java b/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
index ff53858..bbd5f02 100644
--- a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
+++ b/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
@@ -42,6 +42,8 @@
 
 public class ImsServiceProxyCompat implements IMMTelFeature {
 
+    private static final int SERVICE_ID = ImsFeature.MMTEL;
+
     protected final int mSlotId;
     protected IBinder mBinder;
 
@@ -65,29 +67,28 @@
     }
 
     @Override
-    public boolean isConnected(int sessionId, int callServiceType, int callType)
+    public boolean isConnected(int callServiceType, int callType)
             throws RemoteException {
         checkBinderConnection();
-        return getServiceInterface(mBinder).isConnected(sessionId,  callServiceType, callType);
+        return getServiceInterface(mBinder).isConnected(SERVICE_ID,  callServiceType, callType);
     }
 
     @Override
-    public boolean isOpened(int sessionId) throws RemoteException {
+    public boolean isOpened() throws RemoteException {
         checkBinderConnection();
-        return getServiceInterface(mBinder).isOpened(sessionId);
+        return getServiceInterface(mBinder).isOpened(SERVICE_ID);
     }
 
     @Override
-    public void addRegistrationListener(int sessionId, IImsRegistrationListener listener)
+    public void addRegistrationListener(IImsRegistrationListener listener)
             throws RemoteException {
         checkBinderConnection();
         getServiceInterface(mBinder).addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener);
     }
 
     @Override
-    public void removeRegistrationListener(int sessionId, IImsRegistrationListener listener)
+    public void removeRegistrationListener(IImsRegistrationListener listener)
             throws RemoteException {
-        checkBinderConnection();
         // Not Implemented in old ImsService. If the registration listener becomes invalid, the
         // ImsService will remove.
     }
@@ -114,46 +115,46 @@
     }
 
     @Override
-    public IImsUt getUtInterface(int sessionId) throws RemoteException {
+    public IImsUt getUtInterface() throws RemoteException {
         checkBinderConnection();
-        return getServiceInterface(mBinder).getUtInterface(sessionId);
+        return getServiceInterface(mBinder).getUtInterface(SERVICE_ID);
     }
 
     @Override
-    public IImsConfig getConfigInterface(int sessionId) throws RemoteException {
+    public IImsConfig getConfigInterface() throws RemoteException {
         checkBinderConnection();
         return getServiceInterface(mBinder).getConfigInterface(mSlotId);
     }
 
     @Override
-    public void turnOnIms(int sessionId) throws RemoteException {
+    public void turnOnIms() throws RemoteException {
         checkBinderConnection();
         getServiceInterface(mBinder).turnOnIms(mSlotId);
     }
 
     @Override
-    public void turnOffIms(int sessionId) throws RemoteException {
+    public void turnOffIms() throws RemoteException {
         checkBinderConnection();
         getServiceInterface(mBinder).turnOffIms(mSlotId);
     }
 
     @Override
-    public IImsEcbm getEcbmInterface(int sessionId) throws RemoteException {
+    public IImsEcbm getEcbmInterface() throws RemoteException {
         checkBinderConnection();
-        return getServiceInterface(mBinder).getEcbmInterface(sessionId);
+        return getServiceInterface(mBinder).getEcbmInterface(SERVICE_ID);
     }
 
     @Override
-    public void setUiTTYMode(int sessionId, int uiTtyMode, Message onComplete)
+    public void setUiTTYMode(int uiTtyMode, Message onComplete)
             throws RemoteException {
         checkBinderConnection();
-        getServiceInterface(mBinder).setUiTTYMode(sessionId, uiTtyMode, onComplete);
+        getServiceInterface(mBinder).setUiTTYMode(SERVICE_ID, uiTtyMode, onComplete);
     }
 
     @Override
-    public IImsMultiEndpoint getMultiEndpointInterface(int sessionId) throws RemoteException {
+    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
         checkBinderConnection();
-        return getServiceInterface(mBinder).getMultiEndpointInterface(sessionId);
+        return getServiceInterface(mBinder).getMultiEndpointInterface(SERVICE_ID);
     }
 
     /**
diff --git a/telephony/java/android/telephony/ims/feature/IMMTelFeature.java b/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
index e180843..d65e27e 100644
--- a/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
@@ -68,7 +68,6 @@
      * Checks if the IMS service has successfully registered to the IMS network with the specified
      * service & call type.
      *
-     * @param sessionId a session id which is obtained from {@link #startSession}
      * @param callServiceType a service type that is specified in {@link ImsCallProfile}
      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
@@ -80,31 +79,28 @@
      * @return true if the specified service id is connected to the IMS network; false otherwise
      * @throws RemoteException
      */
-    boolean isConnected(int sessionId, int callServiceType, int callType) throws RemoteException;
+    boolean isConnected(int callServiceType, int callType) throws RemoteException;
 
     /**
      * Checks if the specified IMS service is opened.
      *
-     * @param sessionId a service id which is obtained from {@link #startSession}
      * @return true if the specified service id is opened; false otherwise
      */
-    boolean isOpened(int sessionId) throws RemoteException;
+    boolean isOpened() throws RemoteException;
 
     /**
      * Add a new registration listener for the client associated with the session Id.
-     * @param sessionId a session id which is obtained from {@link #startSession}
      * @param listener An implementation of IImsRegistrationListener.
      */
-    void addRegistrationListener(int sessionId, IImsRegistrationListener listener)
+    void addRegistrationListener(IImsRegistrationListener listener)
             throws RemoteException;
 
     /**
      * Remove a previously registered listener using {@link #addRegistrationListener} for the client
      * associated with the session Id.
-     * @param sessionId a session id which is obtained from {@link #startSession}
      * @param listener A previously registered IImsRegistrationListener
      */
-    void removeRegistrationListener(int sessionId, IImsRegistrationListener listener)
+    void removeRegistrationListener(IImsRegistrationListener listener)
             throws RemoteException;
 
     /**
@@ -152,41 +148,40 @@
     /**
      * @return The Ut interface for the supplementary service configuration.
      */
-    IImsUt getUtInterface(int sessionId) throws RemoteException;
+    IImsUt getUtInterface() throws RemoteException;
 
     /**
      * @return The config interface for IMS Configuration
      */
-    IImsConfig getConfigInterface(int sessionId) throws RemoteException;
+    IImsConfig getConfigInterface() throws RemoteException;
 
     /**
      * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
      * @param sessionId a session id which is obtained from {@link #startSession}
      */
-    void turnOnIms(int sessionId) throws RemoteException;
+    void turnOnIms() throws RemoteException;
 
     /**
      * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
      * @param sessionId a session id which is obtained from {@link #startSession}
      */
-    void turnOffIms(int sessionId) throws RemoteException;
+    void turnOffIms() throws RemoteException;
 
     /**
      * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
      */
-    IImsEcbm getEcbmInterface(int sessionId) throws RemoteException;
+    IImsEcbm getEcbmInterface() throws RemoteException;
 
     /**
      * Sets the current UI TTY mode for the MMTelFeature.
-     * @param sessionId a session id which is obtained from {@link #startSession}
      * @param uiTtyMode An integer containing the new UI TTY Mode.
      * @param onComplete A {@link Message} to be used when the mode has been set.
      * @throws RemoteException
      */
-    void setUiTTYMode(int sessionId, int uiTtyMode, Message onComplete) throws RemoteException;
+    void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException;
 
     /**
      * @return MultiEndpoint interface for DEP notifications
      */
-    IImsMultiEndpoint getMultiEndpointInterface(int sessionId) throws RemoteException;
+    IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException;
 }
diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
index 570cd65..a71f0bf 100644
--- a/telephony/java/android/telephony/ims/feature/MMTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
@@ -50,21 +50,21 @@
     }
 
     @Override
-    public boolean isConnected(int sessionId, int callSessionType, int callType) {
+    public boolean isConnected(int callSessionType, int callType) {
         return false;
     }
 
     @Override
-    public boolean isOpened(int sessionId) {
+    public boolean isOpened() {
         return false;
     }
 
     @Override
-    public void addRegistrationListener(int sessionId, IImsRegistrationListener listener) {
+    public void addRegistrationListener(IImsRegistrationListener listener) {
     }
 
     @Override
-    public void removeRegistrationListener(int sessionId, IImsRegistrationListener listener) {
+    public void removeRegistrationListener(IImsRegistrationListener listener) {
     }
 
     @Override
@@ -84,34 +84,34 @@
     }
 
     @Override
-    public IImsUt getUtInterface(int sessionId) {
+    public IImsUt getUtInterface() {
         return null;
     }
 
     @Override
-    public IImsConfig getConfigInterface(int sessionId) {
+    public IImsConfig getConfigInterface() {
         return null;
     }
 
     @Override
-    public void turnOnIms(int sessionId) {
+    public void turnOnIms() {
     }
 
     @Override
-    public void turnOffIms(int sessionId) {
+    public void turnOffIms() {
     }
 
     @Override
-    public IImsEcbm getEcbmInterface(int sessionId) {
+    public IImsEcbm getEcbmInterface() {
         return null;
     }
 
     @Override
-    public void setUiTTYMode(int sessionId, int uiTtyMode, Message onComplete) {
+    public void setUiTTYMode(int uiTtyMode, Message onComplete) {
     }
 
     @Override
-    public IImsMultiEndpoint getMultiEndpointInterface(int sessionId) {
+    public IImsMultiEndpoint getMultiEndpointInterface() {
         return null;
     }
 
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index b700f49..712816f 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -42,24 +42,23 @@
     int startSession(int slotId, int featureType, in PendingIntent incomingCallIntent,
             in IImsRegistrationListener listener);
     void endSession(int slotId, int featureType, int sessionId);
-    boolean isConnected(int slotId, int featureType, int sessionId, int callSessionType, int callType);
-    boolean isOpened(int slotId, int featureType, int sessionId);
+    boolean isConnected(int slotId, int featureType, int callSessionType, int callType);
+    boolean isOpened(int slotId, int featureType);
     int getFeatureStatus(int slotId, int featureType);
-    void addRegistrationListener(int slotId, int featureType, int sessionId,
+    void addRegistrationListener(int slotId, int featureType, in IImsRegistrationListener listener);
+    void removeRegistrationListener(int slotId, int featureType,
             in IImsRegistrationListener listener);
-    void removeRegistrationListener(int slotId, int featureType, int sessionId,
-            in IImsRegistrationListener listener);
-    ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId, int callSessionType, int callType);
+    ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId,
+            int callSessionType, int callType);
     IImsCallSession createCallSession(int slotId, int featureType, int sessionId,
             in ImsCallProfile profile, IImsCallSessionListener listener);
     IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId,
             String callId);
-    IImsUt getUtInterface(int slotId, int featureType, int sessionId);
-    IImsConfig getConfigInterface(int slotId, int featureType, int sessionId);
-    void turnOnIms(int slotId, int featureType, int sessionId);
-    void turnOffIms(int slotId, int featureType, int sessionId);
-    IImsEcbm getEcbmInterface(int slotId, int featureType, int sessionId);
-    void setUiTTYMode(int slotId, int featureType, int sessionId, int uiTtyMode,
-            in Message onComplete);
-    IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType, int sessionId);
+    IImsUt getUtInterface(int slotId, int featureType);
+    IImsConfig getConfigInterface(int slotId, int featureType);
+    void turnOnIms(int slotId, int featureType);
+    void turnOffIms(int slotId, int featureType);
+    IImsEcbm getEcbmInterface(int slotId, int featureType);
+    void setUiTTYMode(int slotId, int featureType, int uiTtyMode, in Message onComplete);
+    IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e6a6178..88daf64 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1287,4 +1287,12 @@
      * @hide
      */
     List<ClientRequestStats> getClientRequestStats(String callingPackage, int subid);
+
+    /**
+     * Set SIM card power state. Request is equivalent to inserting or removing the card.
+     * @param slotId SIM slot id
+     * @param powerUp True if powering up the SIM, otherwise powering down
+     * @hide
+     * */
+    void setSimPowerStateForSlot(int slotId, boolean powerUp);
 }