Merge "Notify NPMS when uid becomes active or idle."
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 7f7474c..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);
@@ -36246,9 +36244,10 @@
   }
 
   public static final class Dataset.Builder {
-    ctor public Dataset.Builder(java.lang.CharSequence);
+    ctor public Dataset.Builder();
     method public android.service.autofill.Dataset build();
     method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
+    method public android.service.autofill.Dataset.Builder setPresentation(android.widget.RemoteViews);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
   }
 
@@ -36270,6 +36269,7 @@
     method public android.service.autofill.FillResponse build();
     method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
     method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+    method public android.service.autofill.FillResponse.Builder setPresentation(android.widget.RemoteViews);
   }
 
   public final class SaveCallback {
@@ -45637,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);
@@ -45645,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 {
@@ -50517,11 +50517,9 @@
     method public void removeTextChangedListener(android.text.TextWatcher);
     method public void setAllCaps(boolean);
     method public final void setAutoLinkMask(int);
-    method public void setAutoSizeMaxTextSize(int, float);
-    method public void setAutoSizeMinTextSize(int, float);
-    method public void setAutoSizeStepGranularity(int, float);
-    method public void setAutoSizeTextPresetSizes(int[]);
-    method public void setAutoSizeTextType(int);
+    method public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+    method public void setAutoSizeTextTypeWithDefaults(int);
     method public void setBreakStrategy(int);
     method public void setCompoundDrawablePadding(int);
     method public void setCompoundDrawableTintList(android.content.res.ColorStateList);
@@ -51238,7 +51236,7 @@
     method public static dalvik.system.DexFile loadDex(java.lang.String, java.lang.String, int) throws java.io.IOException;
   }
 
-  public final class InMemoryDexClassLoader extends java.lang.ClassLoader {
+  public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader {
     ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
   }
 
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 4aca489..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);
@@ -39326,9 +39361,10 @@
   }
 
   public static final class Dataset.Builder {
-    ctor public Dataset.Builder(java.lang.CharSequence);
+    ctor public Dataset.Builder();
     method public android.service.autofill.Dataset build();
     method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
+    method public android.service.autofill.Dataset.Builder setPresentation(android.widget.RemoteViews);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
   }
 
@@ -39350,6 +39386,7 @@
     method public android.service.autofill.FillResponse build();
     method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
     method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+    method public android.service.autofill.FillResponse.Builder setPresentation(android.widget.RemoteViews);
   }
 
   public final class SaveCallback {
@@ -49142,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);
@@ -49150,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 {
@@ -54386,11 +54423,9 @@
     method public void removeTextChangedListener(android.text.TextWatcher);
     method public void setAllCaps(boolean);
     method public final void setAutoLinkMask(int);
-    method public void setAutoSizeMaxTextSize(int, float);
-    method public void setAutoSizeMinTextSize(int, float);
-    method public void setAutoSizeStepGranularity(int, float);
-    method public void setAutoSizeTextPresetSizes(int[]);
-    method public void setAutoSizeTextType(int);
+    method public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+    method public void setAutoSizeTextTypeWithDefaults(int);
     method public void setBreakStrategy(int);
     method public void setCompoundDrawablePadding(int);
     method public void setCompoundDrawableTintList(android.content.res.ColorStateList);
@@ -55107,7 +55142,7 @@
     method public static dalvik.system.DexFile loadDex(java.lang.String, java.lang.String, int) throws java.io.IOException;
   }
 
-  public final class InMemoryDexClassLoader extends java.lang.ClassLoader {
+  public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader {
     ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
   }
 
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 9f7b8e9..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);
@@ -36385,9 +36383,10 @@
   }
 
   public static final class Dataset.Builder {
-    ctor public Dataset.Builder(java.lang.CharSequence);
+    ctor public Dataset.Builder();
     method public android.service.autofill.Dataset build();
     method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
+    method public android.service.autofill.Dataset.Builder setPresentation(android.widget.RemoteViews);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
   }
 
@@ -36409,6 +36408,7 @@
     method public android.service.autofill.FillResponse build();
     method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
     method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+    method public android.service.autofill.FillResponse.Builder setPresentation(android.widget.RemoteViews);
   }
 
   public final class SaveCallback {
@@ -45999,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);
@@ -46007,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 {
@@ -50888,11 +50888,9 @@
     method public void removeTextChangedListener(android.text.TextWatcher);
     method public void setAllCaps(boolean);
     method public final void setAutoLinkMask(int);
-    method public void setAutoSizeMaxTextSize(int, float);
-    method public void setAutoSizeMinTextSize(int, float);
-    method public void setAutoSizeStepGranularity(int, float);
-    method public void setAutoSizeTextPresetSizes(int[]);
-    method public void setAutoSizeTextType(int);
+    method public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+    method public void setAutoSizeTextTypeWithDefaults(int);
     method public void setBreakStrategy(int);
     method public void setCompoundDrawablePadding(int);
     method public void setCompoundDrawableTintList(android.content.res.ColorStateList);
@@ -51617,7 +51615,7 @@
     method public static dalvik.system.DexFile loadDex(java.lang.String, java.lang.String, int) throws java.io.IOException;
   }
 
-  public final class InMemoryDexClassLoader extends java.lang.ClassLoader {
+  public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader {
     ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
   }
 
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/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 9e486d5..b4e119e 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -52,6 +52,8 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
+import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+
 /**
  * Accessibility services should only be used to assist users with disabilities in using
  * Android devices and apps. They run in the background and receive callbacks by the system
@@ -618,7 +620,8 @@
      */
     @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT)
     public final @Nullable FingerprintGestureController getFingerprintGestureController() {
-        if (mFingerprintGestureController == null) {
+        if ((mFingerprintGestureController == null)
+                && getPackageManager().hasSystemFeature(FEATURE_FINGERPRINT)) {
             FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class);
             if ((fingerprintManager != null) && fingerprintManager.isHardwareDetected()) {
                 AccessibilityServiceInfo info = getServiceInfo();
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index e135ffd..19d1a7d 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -47,6 +47,8 @@
 import java.util.Collections;
 import java.util.List;
 
+import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+
 /**
  * This class describes an {@link AccessibilityService}. The system notifies an
  * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
@@ -1042,8 +1044,7 @@
                     new CapabilityInfo(CAPABILITY_CAN_PERFORM_GESTURES,
                             R.string.capability_title_canPerformGestures,
                             R.string.capability_desc_canPerformGestures));
-            if ((context == null)
-                    || context.getSystemService(FingerprintManager.class).isHardwareDetected()) {
+            if ((context == null) || fingerprintAvailable(context)) {
                 sAvailableCapabilityInfos.put(CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES,
                         new CapabilityInfo(CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES,
                                 R.string.capability_title_canCaptureFingerprintGestures,
@@ -1053,6 +1054,10 @@
         return sAvailableCapabilityInfos;
     }
 
+    private static boolean fingerprintAvailable(Context context) {
+        return context.getPackageManager().hasSystemFeature(FEATURE_FINGERPRINT)
+                && context.getSystemService(FingerprintManager.class).isHardwareDetected();
+    }
     /**
      * @hide
      */
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index c56772e..6fc60e9 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7039,7 +7039,8 @@
                 }
             }
         } else if (who.startsWith(AUTO_FILL_AUTH_WHO_PREFIX)) {
-            getSystemService(AutoFillManager.class).onAuthenticationResult(data);
+            Intent resultData = (resultCode == Activity.RESULT_OK) ? data : null;
+            getSystemService(AutoFillManager.class).onAuthenticationResult(resultData);
         } else {
             Fragment frag = mFragments.findFragmentByWho(who);
             if (frag != null) {
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 739015f..8ad7810 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -2807,40 +2807,56 @@
     
     public void dispatchCreate() {
         mStateSaved = false;
+        mExecutingActions = true;
         moveToState(Fragment.CREATED, false);
+        mExecutingActions = false;
     }
     
     public void dispatchActivityCreated() {
         mStateSaved = false;
+        mExecutingActions = true;
         moveToState(Fragment.ACTIVITY_CREATED, false);
+        mExecutingActions = false;
     }
     
     public void dispatchStart() {
         mStateSaved = false;
+        mExecutingActions = true;
         moveToState(Fragment.STARTED, false);
+        mExecutingActions = false;
     }
     
     public void dispatchResume() {
         mStateSaved = false;
+        mExecutingActions = true;
         moveToState(Fragment.RESUMED, false);
+        mExecutingActions = false;
     }
     
     public void dispatchPause() {
+        mExecutingActions = true;
         moveToState(Fragment.STARTED, false);
+        mExecutingActions = false;
     }
     
     public void dispatchStop() {
+        mExecutingActions = true;
         moveToState(Fragment.STOPPED, false);
+        mExecutingActions = false;
     }
     
     public void dispatchDestroyView() {
+        mExecutingActions = true;
         moveToState(Fragment.CREATED, false);
+        mExecutingActions = false;
     }
 
     public void dispatchDestroy() {
         mDestroyed = true;
         execPendingActions();
+        mExecutingActions = true;
         moveToState(Fragment.INITIALIZING, false);
+        mExecutingActions = false;
         mHost = null;
         mContainer = null;
         mParent = null;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9cb3dd6..2ace0a2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6343,6 +6343,7 @@
      * The settings that can be updated by a profile or device owner with this method are:
      * <ul>
      * <li>{@link Settings.Secure#DEFAULT_INPUT_METHOD}</li>
+     * <li>{@link Settings.Secure#INSTALL_NON_MARKET_APPS}</li>
      * <li>{@link Settings.Secure#SKIP_FIRST_USE_HINTS}</li>
      * </ul>
      * <p>
@@ -6351,15 +6352,6 @@
      * <li>{@link Settings.Secure#LOCATION_MODE}</li>
      * </ul>
      *
-     * <strong>Note: Starting from Android O, apps should no longer call this method with the
-     * setting {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS}, which is
-     * deprecated. Instead, device owners or profile owners should use the restriction
-     * {@link UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES}.
-     * If any app targeting {@link android.os.Build.VERSION_CODES#O} or higher calls this method
-     * with {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS},
-     * an {@link UnsupportedOperationException} is thrown.
-     * </strong>
-     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param setting The name of the setting to update.
      * @param value The value to update the setting to.
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/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2664dc6..56f1e0c 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2745,15 +2745,15 @@
         String cls = clsSeq.toString();
         char c = cls.charAt(0);
         if (c == '.') {
-            return (pkg + cls).intern();
+            return pkg + cls;
         }
         if (cls.indexOf('.') < 0) {
             StringBuilder b = new StringBuilder(pkg);
             b.append('.');
             b.append(cls);
-            return b.toString().intern();
+            return b.toString();
         }
-        return cls.intern();
+        return cls;
     }
 
     private static String buildCompoundName(String pkg,
@@ -2773,7 +2773,7 @@
                         + pkg + ": " + nameError;
                 return null;
             }
-            return (pkg + proc).intern();
+            return pkg + proc;
         }
         String nameError = validateName(proc, true, false);
         if (nameError != null && !"system".equals(proc)) {
@@ -2781,7 +2781,7 @@
                     + pkg + ": " + nameError;
             return null;
         }
-        return proc.intern();
+        return proc;
     }
 
     private static String buildProcessName(String pkg, String defProc,
@@ -5083,7 +5083,7 @@
             if (v != null) {
                 if (v.type == TypedValue.TYPE_STRING) {
                     CharSequence cs = v.coerceToString();
-                    data.putString(name, cs != null ? cs.toString().intern() : null);
+                    data.putString(name, cs != null ? cs.toString() : null);
                 } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
                     data.putBoolean(name, v.data != 0);
                 } else if (v.type >= TypedValue.TYPE_FIRST_INT
@@ -5866,7 +5866,7 @@
             // We use the boot classloader for all classes that we load.
             final ClassLoader boot = Object.class.getClassLoader();
 
-            packageName = dest.readString();
+            packageName = dest.readString().intern();
             manifestPackageName = dest.readString();
             splitNames = dest.readStringArray();
             volumeUuid = dest.readString();
@@ -5879,6 +5879,9 @@
             splitPrivateFlags = dest.createIntArray();
             baseHardwareAccelerated = (dest.readInt() == 1);
             applicationInfo = dest.readParcelable(boot);
+            if (applicationInfo.permission != null) {
+                applicationInfo.permission = applicationInfo.permission.intern();
+            }
 
             // We don't serialize the "owner" package and the application info object for each of
             // these components, in order to save space and to avoid circular dependencies while
@@ -5899,7 +5902,10 @@
             fixupOwner(instrumentation);
 
             dest.readStringList(requestedPermissions);
+            internStringArrayList(requestedPermissions);
             protectedBroadcasts = dest.createStringArrayList();
+            internStringArrayList(protectedBroadcasts);
+
             parentPackage = dest.readParcelable(boot);
 
             childPackages = new ArrayList<>();
@@ -5909,16 +5915,23 @@
             }
 
             staticSharedLibName = dest.readString();
+            if (staticSharedLibName != null) {
+                staticSharedLibName = staticSharedLibName.intern();
+            }
             staticSharedLibVersion = dest.readInt();
             libraryNames = dest.createStringArrayList();
+            internStringArrayList(libraryNames);
             usesLibraries = dest.createStringArrayList();
+            internStringArrayList(usesLibraries);
             usesOptionalLibraries = dest.createStringArrayList();
+            internStringArrayList(usesOptionalLibraries);
             usesLibraryFiles = dest.readStringArray();
 
             final int libCount = dest.readInt();
             if (libCount > 0) {
                 usesStaticLibraries = new ArrayList<>(libCount);
                 dest.readStringList(usesStaticLibraries);
+                internStringArrayList(usesStaticLibraries);
                 usesStaticLibrariesVersions = new int[libCount];
                 dest.readIntArray(usesStaticLibrariesVersions);
                 usesStaticLibrariesCertDigests = new String[libCount];
@@ -5937,7 +5950,13 @@
             mAppMetaData = dest.readBundle();
             mVersionCode = dest.readInt();
             mVersionName = dest.readString();
+            if (mVersionName != null) {
+                mVersionName = mVersionName.intern();
+            }
             mSharedUserId = dest.readString();
+            if (mSharedUserId != null) {
+                mSharedUserId = mSharedUserId.intern();
+            }
             mSharedUserLabel = dest.readInt();
 
             mSignatures = (Signature[]) dest.readParcelableArray(boot, Signature.class);
@@ -5988,6 +6007,15 @@
             restrictUpdateHash = dest.createByteArray();
         }
 
+        private static void internStringArrayList(List<String> list) {
+            if (list != null) {
+                final int N = list.size();
+                for (int i = 0; i < N; ++i) {
+                    list.set(i, list.get(i).intern());
+                }
+            }
+        }
+
         /**
          * Sets the package owner and the the {@code applicationInfo} for every component
          * owner by this package.
@@ -6375,6 +6403,10 @@
             super(in);
             final ClassLoader boot = Object.class.getClassLoader();
             info = in.readParcelable(boot);
+            if (info.group != null) {
+                info.group = info.group.intern();
+            }
+
             tree = (in.readInt() == 1);
             group = in.readParcelable(boot);
         }
@@ -6651,6 +6683,10 @@
             for (ActivityIntentInfo aii : intents) {
                 aii.activity = this;
             }
+
+            if (info.permission != null) {
+                info.permission = info.permission.intern();
+            }
         }
 
         public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Activity>() {
@@ -6735,6 +6771,10 @@
             for (ServiceIntentInfo aii : intents) {
                 aii.service = this;
             }
+
+            if (info.permission != null) {
+                info.permission = info.permission.intern();
+            }
         }
 
         public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Service>() {
@@ -6816,6 +6856,18 @@
             for (ProviderIntentInfo aii : intents) {
                 aii.provider = this;
             }
+
+            if (info.readPermission != null) {
+                info.readPermission = info.readPermission.intern();
+            }
+
+            if (info.writePermission != null) {
+                info.writePermission = info.writePermission.intern();
+            }
+
+            if (info.authority != null) {
+                info.authority = info.authority.intern();
+            }
         }
 
         public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Provider>() {
@@ -6888,6 +6940,14 @@
         private Instrumentation(Parcel in) {
             super(in);
             info = in.readParcelable(Object.class.getClassLoader());
+
+            if (info.targetPackage != null) {
+                info.targetPackage = info.targetPackage.intern();
+            }
+
+            if (info.targetProcess != null) {
+                info.targetProcess = info.targetProcess.intern();
+            }
         }
 
         public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Instrumentation>() {
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/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 600d82f..c0c5db6 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -200,6 +200,10 @@
 
     private ViewGroup mPrefsContainer;
 
+    // Backup of the original activity title. This is used when navigating back to the headers list
+    // in onBackPress to restore the title.
+    private CharSequence mActivityTitle;
+
     // Null if in legacy mode.
     private ViewGroup mHeadersContainer;
 
@@ -569,6 +573,7 @@
         Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
         int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
         int initialShortTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, 0);
+        mActivityTitle = getTitle();
 
         if (savedInstanceState != null) {
             // We are restarting from a previous saved state; used that to
@@ -704,6 +709,9 @@
 
             mPrefsContainer.setVisibility(View.GONE);
             mHeadersContainer.setVisibility(View.VISIBLE);
+            if (mActivityTitle != null) {
+                showBreadCrumbs(mActivityTitle, null);
+            }
             getListView().clearChoices();
         } else {
             super.onBackPressed();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7c8e264..ebc5b88 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5217,18 +5217,6 @@
         public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
 
         /**
-         * A flag to tell {@link com.android.server.devicepolicy.DevicePolicyManagerService} that
-         * the default for {@link #INSTALL_NON_MARKET_APPS} is reversed for this user on OTA. So it
-         * can set the restriction {@link android.os.UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES}
-         * on behalf of the profile owner if needed to make the change transparent for profile
-         * owners.
-         *
-         * @hide
-         */
-        public static final String UNKNOWN_SOURCES_DEFAULT_REVERSED =
-                "unknown_sources_default_reversed";
-
-        /**
          * Comma-separated list of location providers that activities may access. Do not rely on
          * this value being present in settings.db or on ContentObserver notifications on the
          * corresponding Uri.
@@ -8590,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/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index d06f528..59b494c 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -25,7 +25,7 @@
 import android.os.Parcelable;
 import android.view.autofill.AutoFillId;
 import android.view.autofill.AutoFillValue;
-
+import android.widget.RemoteViews;
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
@@ -36,33 +36,28 @@
  * <p>It contains:
  *
  * <ol>
- *   <li>A name used to identify the dataset in the UI.
- *   <li>A list of id/value pairs for the fields that can be auto-filled.
- *   <li>A list of savable ids in addition to the ones with a provided value.
+ *   <li>A list of values for input fields.
+ *   <li>A presentation view to visualize.
+ *   <li>An optional intent to authenticate.
  * </ol>
  *
  * @see android.service.autofill.FillResponse for examples.
  */
 public final class Dataset implements Parcelable {
 
-    private final CharSequence mName;
     private final ArrayList<AutoFillId> mFieldIds;
     private final ArrayList<AutoFillValue> mFieldValues;
+    private final RemoteViews mPresentation;
     private final IntentSender mAuthentication;
 
     private Dataset(Builder builder) {
-        mName = builder.mName;
         mFieldIds = builder.mFieldIds;
         mFieldValues = builder.mFieldValues;
+        mPresentation = builder.mPresentation;
         mAuthentication = builder.mAuthentication;
     }
 
     /** @hide */
-    public @NonNull CharSequence getName() {
-        return mName;
-    }
-
-    /** @hide */
     public @Nullable ArrayList<AutoFillId> getFieldIds() {
         return mFieldIds;
     }
@@ -73,6 +68,11 @@
     }
 
     /** @hide */
+    public @Nullable RemoteViews getPresentation() {
+        return mPresentation;
+    }
+
+    /** @hide */
     public @Nullable IntentSender getAuthentication() {
         return mAuthentication;
     }
@@ -86,11 +86,12 @@
     public String toString() {
         if (!DEBUG) return super.toString();
 
-        final StringBuilder builder = new StringBuilder("Dataset [name=").append(mName)
+        return new StringBuilder("Dataset [")
                 .append(", fieldIds=").append(mFieldIds)
                 .append(", fieldValues=").append(mFieldValues)
-                .append(", hasAuthentication=").append(mAuthentication != null);
-        return builder.append(']').toString();
+                .append(", hasPresentation=").append(mPresentation != null)
+                .append(", hasAuthentication=").append(mAuthentication != null)
+                .append(']').toString();
     }
 
     /**
@@ -98,21 +99,22 @@
      * one value for a field or set an authentication intent.
      */
     public static final class Builder {
-        private CharSequence mName;
         private ArrayList<AutoFillId> mFieldIds;
         private ArrayList<AutoFillValue> mFieldValues;
+        private RemoteViews mPresentation;
         private IntentSender mAuthentication;
         private boolean mDestroyed;
 
         /**
-         * Creates a new builder.
+         * Sets the presentation used to visualize this dataset.
          *
-         * @param name Name used to identify the dataset in the UI. Typically it's the same value as
-         * the first field in the dataset (like username or email address) or a user-provided name
-         * (like "My Work Address").
+         * @param presentation The presentation view.
+         *
+         * @return This builder.
          */
-        public Builder(@NonNull CharSequence name) {
-            mName = Preconditions.checkStringNotEmpty(name, "name cannot be empty or null");
+        public @NonNull Builder setPresentation(@Nullable RemoteViews presentation) {
+            mPresentation = presentation;
+            return this;
         }
 
         /**
@@ -121,7 +123,7 @@
          * <p>This method is called when you need to provide an authentication
          * UI for the data set. For example, when a data set contains credit card information
          * (such as number, expiration date, and verification code), you can display UI
-         * asking for the verification code to before filing in the data). Even if the
+         * asking for the verification code before filing in the data. Even if the
          * data set is completely populated the system will launch the specified authentication
          * intent and will need your approval to fill it in. Since the data set is "locked"
          * until the user authenticates it, typically this data set name is masked
@@ -138,7 +140,7 @@
          * android.app.Activity#RESULT_OK} and provide the fully populated {@link Dataset
          * dataset} by setting it to the {@link
          * android.view.autofill.AutoFillManager#EXTRA_AUTHENTICATION_RESULT} extra. For example,
-         * if you provided an credit card information without the CVV for the data set in the
+         * if you provided credit card information without the CVV for the data set in the
          * {@link FillResponse response} then the returned data set should contain the
          * CVV entry.</p>
          *
@@ -147,6 +149,7 @@
          * platform needs to fill in the authentication arguments.</p>
          *
          * @param authentication Intent to an activity with your authentication flow.
+         * @return This builder.
          *
          * @see android.app.PendingIntent
          */
@@ -162,6 +165,7 @@
          * @param id id returned by {@link
          *         android.app.assist.AssistStructure.ViewNode#getAutoFillId()}.
          * @param value value to be auto filled.
+         * @return This builder.
          */
         public @NonNull Builder setValue(@NonNull AutoFillId id, @NonNull AutoFillValue value) {
             throwIfDestroyed();
@@ -184,14 +188,21 @@
 
         /**
          * Creates a new {@link Dataset} instance. You should not interact
-         * with this builder once this method is called.
+         * with this builder once this method is called. It is required
+         * that you specified at least one field. Also it is mandatory to
+         * provide a presentation view to visualize the data set in the UI.
+         *
+         * @return The built dataset.
          */
         public @NonNull Dataset build() {
             throwIfDestroyed();
             mDestroyed = true;
-            if (mFieldIds == null && mAuthentication == null) {
+            if (mFieldIds == null) {
                 throw new IllegalArgumentException(
-                        "at least one value or an authentication must be set");
+                        "at least one value must be set");
+            }
+            if (mPresentation == null) {
+                throw new IllegalArgumentException("presentation must be set");
             }
             return new Dataset(this);
         }
@@ -214,9 +225,9 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeCharSequence(mName);
-        parcel.writeTypedArrayList(mFieldIds, 0);
-        parcel.writeTypedArrayList(mFieldValues, 0);
+        parcel.writeTypedArrayList(mFieldIds, flags);
+        parcel.writeTypedArrayList(mFieldValues, flags);
+        parcel.writeParcelable(mPresentation, flags);
         parcel.writeParcelable(mAuthentication, flags);
     }
 
@@ -226,7 +237,7 @@
             // Always go through the builder to ensure the data ingested by
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
-            final Builder builder = new Builder(parcel.readCharSequence());
+            final Builder builder = new Builder();
             final ArrayList<AutoFillId> ids = parcel.readTypedArrayList(null);
             final ArrayList<AutoFillValue> values = parcel.readTypedArrayList(null);
             final int idCount = (ids != null) ? ids.size() : 0;
@@ -236,6 +247,7 @@
                 AutoFillValue value = (valueCount > i) ? values.get(i) : null;
                 builder.setValue(id, value);
             }
+            builder.setPresentation(parcel.readParcelable(null));
             builder.setAuthentication(parcel.readParcelable(null));
             return builder.build();
         }
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index fe78973..bac4391 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -26,12 +26,12 @@
 import android.util.ArraySet;
 import android.view.autofill.AutoFillId;
 import android.view.autofill.AutoFillManager;
+import android.widget.RemoteViews;
 
 /**
  * Response for a {@link
  * AutoFillService#onFillRequest(android.app.assist.AssistStructure,
- * Bundle, android.os.CancellationSignal, FillCallback)} and
- * authentication requests.
+ * Bundle, android.os.CancellationSignal, FillCallback)}.
  *
  * <p>The response typically contains one or more {@link Dataset}s, each representing a set of
  * fields that can be auto-filled together, and the Android system displays a dataset picker UI
@@ -43,7 +43,8 @@
  *
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
- *      .add(new Dataset.Builder("homer")
+ *      .add(new Dataset.Builder()
+ *          .setPresentation(createPresentation())
  *          .setTextFieldValue(id1, "homer")
  *          .setTextFieldValue(id2, "D'OH!")
  *          .build())
@@ -54,11 +55,13 @@
  *
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
- *      .add(new Dataset.Builder("Homer's Account")
+ *      .add(new Dataset.Builder()
+ *          .setPresentation(createFirstPresentation())
  *          .setTextFieldValue(id1, "homer")
  *          .setTextFieldValue(id2, "D'OH!")
  *          .build())
- *      .add(new Dataset.Builder("Bart's Account")
+ *      .add(new Dataset.Builder()
+ *          .setPresentation(createSecondPresentation())
  *          .setTextFieldValue(id1, "elbarto")
  *          .setTextFieldValue(id2, "cowabonga")
  *          .build())
@@ -82,7 +85,8 @@
  *
  * <pre class="prettyprint">
  *   new FillResponse.Builder()
- *       .add(new Dataset.Builder("Homer")
+ *       .add(new Dataset.Builder(")
+ *          .setPresentation(createPresentation())
  *          .setTextFieldValue(id1, "Homer")                  // first name
  *          .setTextFieldValue(id2, "Simpson")                // last name
  *          .setTextFieldValue(id3, "742 Evergreen Terrace")  // street
@@ -110,27 +114,31 @@
  *
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
- *      .add(new Dataset.Builder("Homer")
+ *      .add(new Dataset.Builder()
+ *          .setPresentation(createFirstPresentation())
  *          .setTextFieldValue(id1, "Homer")
  *          .setTextFieldValue(id2, "Simpson")
  *          .build())
- *      .add(new Dataset.Builder("Bart")
+ *      .add(new Dataset.Builder()
+ *          .setPresentation(createSecondPresentation())
  *          .setTextFieldValue(id1, "Bart")
  *          .setTextFieldValue(id2, "Simpson")
  *          .build())
  *      .build();
  * </pre>
  *
- * <p>Then after the user picks the {@code Homer} dataset and taps the {@code Street} field to
+ * <p>Then after the user picks the second dataset and taps the street field to
  * trigger another auto-fill request, the second response could be:
  *
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
- *      .add(new Dataset.Builder("Home")
+ *      .add(new Dataset.Builder()
+ *          .setPresentation(createThirdPresentation())
  *          .setTextFieldValue(id3, "742 Evergreen Terrace")
  *          .setTextFieldValue(id4, "Springfield")
  *          .build())
- *      .add(new Dataset.Builder("Work")
+ *      .add(new Dataset.Builder()
+ *          .setPresentation(createFourthPresentation())
  *          .setTextFieldValue(id3, "Springfield Power Plant")
  *          .setTextFieldValue(id4, "Springfield")
  *          .build())
@@ -141,29 +149,31 @@
  * {@link Dataset} level, prior to auto-filling an activity - see {@link FillResponse.Builder
  * #setAuthentication(IntentSender)} and {@link Dataset.Builder#setAuthentication(IntentSender)}.
  * It is recommended that you encrypt only the sensitive data but leave the labels unencrypted
- * which would allow you to provide the dataset names to the user and if they choose one
- * them challenge the user to onAuthenticate. For example, if the user has a home and a work
- * address the Home and Work labels could be stored unencrypted as they don't have any sensitive
- * data while the address data is in an encrypted storage. If the user chooses Home, then the
- * platform will start your authentication flow. If you encrypt all data and require auth
- * at the response level the user will have to interact with the fill UI to trigger a request
- * for the datasets as they don't see Home and Work options which will trigger your auth
- * flow and after successfully authenticating the user will be presented with the Home and
- * Work options where they can pick one. Hence, you have flexibility how to implement your
- * auth while storing labels non-encrypted and data encrypted provides a better user
- * experience.</p>
+ * which would allow you to provide a dataset presentation views with labels and if the user
+ * chooses one of them challenge the user to authenticate. For example, if the user has a
+ * home and a work address the Home and Work labels could be stored unencrypted as they don't
+ * have any sensitive data while the address data is in an encrypted storage. If the user
+ * chooses Home, then the platform will start your authentication flow. If you encrypt all
+ * data and require auth at the response level the user will have to interact with the fill
+ * UI to trigger a request for the datasets (as they don't see the presentation views for the
+ * possible options) which will start your auth flow and after successfully authenticating
+ * the user will be presented with the Home and Work options to pick one. Hence, you have
+ * flexibility how to implement your auth while storing labels non-encrypted and data
+ * encrypted provides a better user experience.</p>
  */
 public final class FillResponse implements Parcelable {
 
     private final ArraySet<Dataset> mDatasets;
     private final ArraySet<AutoFillId> mSavableIds;
     private final Bundle mExtras;
+    private final RemoteViews mPresentation;
     private final IntentSender mAuthentication;
 
     private FillResponse(@NonNull Builder builder) {
         mDatasets = builder.mDatasets;
         mSavableIds = builder.mSavableIds;
         mExtras = builder.mExtras;
+        mPresentation = builder.mPresentation;
         mAuthentication = builder.mAuthentication;
     }
 
@@ -183,26 +193,42 @@
     }
 
     /** @hide */
+    public @Nullable RemoteViews getPresentation() {
+        return mPresentation;
+    }
+
+    /** @hide */
     public @Nullable IntentSender getAuthentication() {
         return mAuthentication;
     }
 
     /**
      * Builder for {@link FillResponse} objects. You must to provide at least
-     * one dataset or set an authentication intent.
+     * one dataset or set an authentication intent with a presentation view.
      */
     public static final class Builder {
         private ArraySet<Dataset> mDatasets;
         private ArraySet<AutoFillId> mSavableIds;
         private Bundle mExtras;
+        private RemoteViews mPresentation;
         private IntentSender mAuthentication;
         private boolean mDestroyed;
 
         /**
-         * Creates a new {@link FillResponse} builder.
+         * Sets the presentation used to visualize this response. You should
+         * set this only if you need an authentication as this is the only
+         * case the response needs to be presented to the user.
+         *
+         * @param presentation The presentation view.
+         *
+         * @return This builder.
+         *
+         * @see #setAuthentication(IntentSender)
          */
-        public Builder() {
-
+        public @NonNull
+        FillResponse.Builder setPresentation(@Nullable RemoteViews presentation) {
+            mPresentation = presentation;
+            return this;
         }
 
         /**
@@ -215,14 +241,15 @@
          * auth on the data set level leading to a better user experience. Note that if you
          * use sensitive data as a label, for example an email address, then it should also
          * be encrypted. The provided {@link android.app.PendingIntent intent} must be an
-         * activity which implements your authentication flow.</p>
+         * activity which implements your authentication flow. Also if you provide an auth
+         * intent you also need to specify the presentation view to be shown in the fill UI
+         * for the user to trigger your authentication flow.</p>
          *
          * <p>When a user triggers auto-fill, the system launches the provided intent
-         * whose extras will have the {@link
-         * AutoFillManager#EXTRA_ASSIST_STRUCTURE screen
+         * whose extras will have the {@link AutoFillManager#EXTRA_ASSIST_STRUCTURE screen
          * content}. Once you complete your authentication flow you should set the activity
-         * result to {@link android.app.Activity#RESULT_OK} and provide the fully populated {@link
-         * FillResponse response} by setting it to the {@link
+         * result to {@link android.app.Activity#RESULT_OK} and provide the fully populated
+         * {@link FillResponse response} by setting it to the {@link
          * AutoFillManager#EXTRA_AUTHENTICATION_RESULT} extra.
          * For example, if you provided an empty {@link FillResponse resppnse} because the
          * user's data was locked and marked that the response needs an authentication then
@@ -235,8 +262,10 @@
          * platform needs to fill in the authentication arguments.</p>
          *
          * @param authentication Intent to an activity with your authentication flow.
+         * @return This builder.
          *
          * @see android.app.PendingIntent#getIntentSender()
+         * @see #setPresentation(RemoteViews)
          */
         public @NonNull Builder setAuthentication(@Nullable IntentSender authentication) {
             throwIfDestroyed();
@@ -245,10 +274,9 @@
         }
 
         /**
-         * Adds a new {@link Dataset} to this response. Adding a dataset with the
-         * same id updates the existing one.
+         * Adds a new {@link Dataset} to this response.
          *
-         * @throws IllegalArgumentException if a dataset with same {@code name} already exists.
+         * @return This builder.
          */
         public@NonNull Builder addDataset(@Nullable Dataset dataset) {
             throwIfDestroyed();
@@ -258,23 +286,18 @@
             if (mDatasets == null) {
                 mDatasets = new ArraySet<>();
             }
-            final int datasetCount = mDatasets.size();
-            for (int i = 0; i < datasetCount; i++) {
-                if (mDatasets.valueAt(i).getName().equals(dataset.getName())) {
-                    throw new IllegalArgumentException("Duplicate dataset name: "
-                            + dataset.getName());
-                }
-            }
             if (!mDatasets.add(dataset)) {
                 return this;
             }
-            final int fieldCount = dataset.getFieldIds().size();
-            for (int i = 0; i < fieldCount; i++) {
-                final AutoFillId id = dataset.getFieldIds().get(i);
-                if (mSavableIds == null) {
-                    mSavableIds = new ArraySet<>();
+            if (dataset.getFieldIds() != null) {
+                final int fieldCount = dataset.getFieldIds().size();
+                for (int i = 0; i < fieldCount; i++) {
+                    final AutoFillId id = dataset.getFieldIds().get(i);
+                    if (mSavableIds == null) {
+                        mSavableIds = new ArraySet<>();
+                    }
+                    mSavableIds.add(id);
                 }
-                mSavableIds.add(id);
             }
             return this;
         }
@@ -285,7 +308,10 @@
          * android.app.assist.AssistStructure, Bundle, SaveCallback)})
          * but were not indirectly set through {@link #addDataset(Dataset)}.
          *
-         * <p>See {@link FillResponse} for examples.
+         * @param ids The savable ids.
+         * @return This builder.
+         *
+         * @see FillResponse
          */
         public @NonNull Builder addSavableFields(@Nullable AutoFillId... ids) {
             throwIfDestroyed();
@@ -306,10 +332,11 @@
          * manipulate this response. For example, they are passed to subsequent
          * calls to {@link AutoFillService#onFillRequest(
          * android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal,
-         * FillCallback)} and {@link
-         * AutoFillService#onSaveRequest(
-         * android.app.assist.AssistStructure, Bundle,
-         * SaveCallback)}.
+         * FillCallback)} and {@link AutoFillService#onSaveRequest(
+         * android.app.assist.AssistStructure, Bundle, SaveCallback)}.
+         *
+         * @param extras The response extras.
+         * @return This builder.
          */
         public Builder setExtras(Bundle extras) {
             throwIfDestroyed();
@@ -318,10 +345,22 @@
         }
 
         /**
-         * Builds a new {@link FillResponse} instance.
+         * Builds a new {@link FillResponse} instance. You must provide at least
+         * one dataset or some savable ids or an authentication with a presentation
+         * view.
+         *
+         * @return A built response.
          */
         public FillResponse build() {
             throwIfDestroyed();
+            if (mAuthentication == null ^ mPresentation == null) {
+                throw new IllegalArgumentException("authentication and presentation"
+                        + " must be both non-null or null");
+            }
+            if (mAuthentication == null && mDatasets == null && mSavableIds == null) {
+                throw new IllegalArgumentException("need to provide at least one"
+                        + " data set or savable ids or an authentication with a presentation");
+            }
             mDestroyed = true;
             return new FillResponse(this);
         }
@@ -339,12 +378,13 @@
     @Override
     public String toString() {
         if (!DEBUG) return super.toString();
-        final StringBuilder builder = new StringBuilder(
+        return new StringBuilder(
                 "FillResponse: [datasets=").append(mDatasets)
                 .append(", savableIds=").append(mSavableIds)
                 .append(", hasExtras=").append(mExtras != null)
-                .append(", hasAuthentication=").append(mAuthentication != null);
-        return builder.append(']').toString();
+                .append(", hasPresentation=").append(mPresentation != null)
+                .append(", hasAuthentication=").append(mAuthentication != null)
+                .toString();
     }
 
     /////////////////////////////////////
@@ -358,10 +398,11 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeTypedArraySet(mDatasets, 0);
-        parcel.writeTypedArraySet(mSavableIds, 0);
-        parcel.writeParcelable(mExtras, 0);
-        parcel.writeParcelable(mAuthentication, 0);
+        parcel.writeTypedArraySet(mDatasets, flags);
+        parcel.writeTypedArraySet(mSavableIds, flags);
+        parcel.writeParcelable(mExtras, flags);
+        parcel.writeParcelable(mPresentation, flags);
+        parcel.writeParcelable(mAuthentication, flags);
     }
 
     public static final Parcelable.Creator<FillResponse> CREATOR =
@@ -383,6 +424,7 @@
                 builder.addSavableFields(fillIds.valueAt(i));
             }
             builder.setExtras(parcel.readParcelable(null));
+            builder.setPresentation(parcel.readParcelable(null));
             builder.setAuthentication(parcel.readParcelable(null));
             return builder.build();
         }
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/view/autofill/AutoFillValue.java b/core/java/android/view/autofill/AutoFillValue.java
index af70361..d9afa3b 100644
--- a/core/java/android/view/autofill/AutoFillValue.java
+++ b/core/java/android/view/autofill/AutoFillValue.java
@@ -32,7 +32,6 @@
  * {@code sub-type} define its semantics (like a postal address).
  */
 public final class AutoFillValue implements Parcelable {
-
     private final String mText;
     private final int mListIndex;
     private final boolean mToggle;
@@ -100,6 +99,12 @@
         return true;
     }
 
+    /** @hide */
+    public String coerceToString() {
+        // TODO(b/33197203): How can we filter on toggles or list values?
+        return mText;
+    }
+
     @Override
     public String toString() {
         if (!DEBUG) return super.toString();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6fa8428..bfaddaf 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -113,6 +113,7 @@
 import android.text.style.UpdateAppearance;
 import android.text.util.Linkify;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.TypedValue;
@@ -706,16 +707,18 @@
     private static final int DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP = 112;
     // Default value for the step size in pixels.
     private static final int DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX = 1;
+    // Use this to specify that any of the auto-size configuration int values have not been set.
+    private static final int UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE = -1;
     // Auto-size text type.
     private int mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
     // Specify if auto-size text is needed.
     private boolean mNeedsAutoSizeText = false;
     // Step size for auto-sizing in pixels.
-    private int mAutoSizeStepGranularityInPx = 0;
+    private int mAutoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
     // Minimum text size for auto-sizing in pixels.
-    private int mAutoSizeMinTextSizeInPx = 0;
+    private int mAutoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
     // Maximum text size for auto-sizing in pixels.
-    private int mAutoSizeMaxTextSizeInPx = 0;
+    private int mAutoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
     // Contains a (specified or computed) distinct sorted set of text sizes in pixels to pick from
     // when auto-sizing text.
     private int[] mAutoSizeTextSizesInPx = EmptyArray.INT;
@@ -727,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).
@@ -937,11 +944,16 @@
         CharSequence text = "";
         CharSequence hint = null;
         boolean password = false;
+        int autoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+        int autoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+        int autoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
         int inputType = EditorInfo.TYPE_NULL;
         a = theme.obtainStyledAttributes(
                     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);
 
@@ -1083,6 +1095,7 @@
                     break;
 
                 case com.android.internal.R.styleable.TextView_text:
+                    fromResourceId = true;
                     text = a.getText(attr);
                     break;
 
@@ -1304,15 +1317,18 @@
                     break;
 
                 case com.android.internal.R.styleable.TextView_autoSizeStepGranularity:
-                    mAutoSizeStepGranularityInPx = a.getDimensionPixelSize(attr, 0);
+                    autoSizeStepGranularityInPx = a.getDimensionPixelSize(attr,
+                        UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE);
                     break;
 
                 case com.android.internal.R.styleable.TextView_autoSizeMinTextSize:
-                    mAutoSizeMinTextSizeInPx = a.getDimensionPixelSize(attr, 0);
+                    autoSizeMinTextSizeInPx = a.getDimensionPixelSize(attr,
+                        UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE);
                     break;
 
                 case com.android.internal.R.styleable.TextView_autoSizeMaxTextSize:
-                    mAutoSizeMaxTextSizeInPx = a.getDimensionPixelSize(attr, 0);
+                    autoSizeMaxTextSizeInPx = a.getDimensionPixelSize(attr,
+                        UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE);
                     break;
 
                 case com.android.internal.R.styleable.TextView_autoSizePresetSizes:
@@ -1320,7 +1336,7 @@
                     if (autoSizeStepSizeArrayResId > 0) {
                         final TypedArray autoSizePreDefTextSizes = a.getResources()
                                 .obtainTypedArray(autoSizeStepSizeArrayResId);
-                        setupAutoSizePresetSizes(autoSizePreDefTextSizes);
+                        setupAutoSizeUniformPresetSizes(autoSizePreDefTextSizes);
                         autoSizePreDefTextSizes.recycle();
                     }
                     break;
@@ -1558,6 +1574,10 @@
         }
 
         setText(text, bufferType);
+        if (fromResourceId) {
+            mTextFromResource = true;
+        }
+
         if (hint != null) setHint(hint);
 
         /*
@@ -1598,12 +1618,47 @@
             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
         }
 
-        setupAutoSizeText();
+        if (supportsAutoSizeText()) {
+            if (mAutoSizeTextType == AUTO_SIZE_TEXT_TYPE_UNIFORM) {
+                // If uniform auto-size has been specified but preset values have not been set then
+                // replace the auto-size configuration values that have not been specified with the
+                // defaults.
+                if (!mHasPresetAutoSizeValues) {
+                    final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+
+                    if (autoSizeMinTextSizeInPx == UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
+                        autoSizeMinTextSizeInPx = (int) TypedValue.applyDimension(
+                                TypedValue.COMPLEX_UNIT_SP,
+                                DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP,
+                                displayMetrics);
+                    }
+
+                    if (autoSizeMaxTextSizeInPx == UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
+                        autoSizeMaxTextSizeInPx = (int) TypedValue.applyDimension(
+                                TypedValue.COMPLEX_UNIT_SP,
+                                DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP,
+                                displayMetrics);
+                    }
+
+                    if (autoSizeMinTextSizeInPx == UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
+                        autoSizeStepGranularityInPx = DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX;
+                    }
+
+                    validateAndSetAutoSizeTextTypeUniformConfiguration(autoSizeMinTextSizeInPx,
+                            autoSizeMaxTextSizeInPx,
+                            autoSizeStepGranularityInPx);
+                }
+
+                setupAutoSizeText();
+            }
+        } else {
+            mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
+        }
     }
 
     /**
      * Specify whether this widget should automatically scale the text to try to perfectly fit
-     * within the layout bounds by taking into account the auto-size configuration.
+     * within the layout bounds by using the default auto-size configuration.
      *
      * @param autoSizeTextType the type of auto-size. Must be one of
      *        {@link TextView#AUTO_SIZE_TEXT_TYPE_NONE} or
@@ -1613,25 +1668,28 @@
      *
      * @see #getAutoSizeTextType()
      */
-    public void setAutoSizeTextType(@AutoSizeTextType int autoSizeTextType) {
+    public void setAutoSizeTextTypeWithDefaults(@AutoSizeTextType int autoSizeTextType) {
         if (supportsAutoSizeText()) {
             switch (autoSizeTextType) {
                 case AUTO_SIZE_TEXT_TYPE_NONE:
-                    if (mAutoSizeTextType != AUTO_SIZE_TEXT_TYPE_NONE) {
-                        // Clear all auto-size configuration
-                        mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
-                        mAutoSizeMinTextSizeInPx = 0;
-                        mAutoSizeMaxTextSizeInPx = 0;
-                        mAutoSizeStepGranularityInPx = 0;
-                        mAutoSizeTextSizesInPx = EmptyArray.INT;
-                        mNeedsAutoSizeText = false;
-                    }
+                    clearAutoSizeConfiguration();
                     break;
                 case AUTO_SIZE_TEXT_TYPE_UNIFORM:
-                    if (mAutoSizeTextType != AUTO_SIZE_TEXT_TYPE_UNIFORM) {
-                        mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_UNIFORM;
-                        setupAutoSizeText();
-                    }
+                    final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+                    final int autoSizeMinTextSizeInPx = (int) TypedValue.applyDimension(
+                            TypedValue.COMPLEX_UNIT_SP,
+                            DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP,
+                            displayMetrics);
+                    final int autoSizeMaxTextSizeInPx = (int) TypedValue.applyDimension(
+                            TypedValue.COMPLEX_UNIT_SP,
+                            DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP,
+                            displayMetrics);
+
+                    validateAndSetAutoSizeTextTypeUniformConfiguration(
+                            autoSizeMinTextSizeInPx,
+                            autoSizeMaxTextSizeInPx,
+                            DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX);
+                    setupAutoSizeText();
                     break;
                 default:
                     throw new IllegalArgumentException(
@@ -1641,6 +1699,111 @@
     }
 
     /**
+     * Specify whether this widget should automatically scale the text to try to perfectly fit
+     * within the layout bounds. If all the configuration params are valid the type of auto-size is
+     * set to {@link #AUTO_SIZE_TEXT_TYPE_UNIFORM}.
+     *
+     * @param autoSizeMinTextSize the minimum text size available for auto-size
+     * @param autoSizeMaxTextSize the maximum text size available for auto-size
+     * @param autoSizeStepGranularity the auto-size step granularity. It is used in conjunction with
+     *                                the minimum and maximum text size in order to build the set of
+     *                                text sizes the system uses to choose from when auto-sizing
+     * @param unit the desired dimension unit for all sizes above. See {@link TypedValue} for the
+     *             possible dimension units
+     *
+     * @throws IllegalArgumentException if any of the configuration params are invalid.
+     *
+     * @attr ref android.R.styleable#TextView_autoSizeText
+     * @attr ref android.R.styleable#TextView_autoSizeMinTextSize
+     * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
+     * @attr ref android.R.styleable#TextView_autoSizeStepGranularity
+     *
+     * @see #setAutoSizeTextTypeWithDefaults(int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
+     * @see #getAutoSizeMinTextSize()
+     * @see #getAutoSizeMaxTextSize()
+     * @see #getAutoSizeStepGranularity()
+     * @see #getAutoSizeTextAvailableSizes()
+     */
+    public void setAutoSizeTextTypeUniformWithConfiguration(
+            int autoSizeMinTextSize,
+            int autoSizeMaxTextSize,
+            int autoSizeStepGranularity,
+            int unit) throws IllegalArgumentException {
+        if (supportsAutoSizeText()) {
+            final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+            final int autoSizeMinTextSizeInPx = (int) TypedValue.applyDimension(
+                    unit, autoSizeMinTextSize, displayMetrics);
+            final int autoSizeMaxTextSizeInPx = (int) TypedValue.applyDimension(
+                    unit, autoSizeMaxTextSize, displayMetrics);
+            final int autoSizeStepGranularityInPx = (int) TypedValue.applyDimension(
+                    unit, autoSizeStepGranularity, displayMetrics);
+
+            validateAndSetAutoSizeTextTypeUniformConfiguration(autoSizeMinTextSizeInPx,
+                    autoSizeMaxTextSizeInPx,
+                    autoSizeStepGranularityInPx);
+            setupAutoSizeText();
+        }
+    }
+
+    /**
+     * Specify whether this widget should automatically scale the text to try to perfectly fit
+     * within the layout bounds. If at least one value from the <code>presetSizes</code> is valid
+     * then the type of auto-size is set to {@link #AUTO_SIZE_TEXT_TYPE_UNIFORM}.
+     *
+     * @param presetSizes an {@code int} array of sizes in pixels
+     * @param unit the desired dimension unit for the preset sizes above. See {@link TypedValue} for
+     *             the possible dimension units
+     *
+     * @throws IllegalArgumentException if all of the <code>presetSizes</code> are invalid.
+     *
+     * @attr ref android.R.styleable#TextView_autoSizeText
+     * @attr ref android.R.styleable#TextView_autoSizePresetSizes
+     *
+     * @see #setAutoSizeTextTypeWithDefaults(int)
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #getAutoSizeMinTextSize()
+     * @see #getAutoSizeMaxTextSize()
+     * @see #getAutoSizeTextAvailableSizes()
+     */
+    public void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit)
+            throws IllegalArgumentException {
+        if (supportsAutoSizeText()) {
+            final int presetSizesLength = presetSizes.length;
+            if (presetSizesLength > 0) {
+                int[] presetSizesInPx = new int[presetSizesLength];
+
+                if (unit == TypedValue.COMPLEX_UNIT_PX) {
+                    presetSizesInPx = Arrays.copyOf(presetSizes, presetSizesLength);
+                } else {
+                    final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+                    // Convert all to sizes to pixels.
+                    for (int i = 0; i < presetSizesLength; i++) {
+                        presetSizesInPx[i] = (int) TypedValue.applyDimension(unit, presetSizes[i],
+                            displayMetrics);
+                    }
+                }
+
+                mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(presetSizesInPx);
+                final int sizesLength = mAutoSizeTextSizesInPx.length;
+                mHasPresetAutoSizeValues = sizesLength > 0;
+                if (mHasPresetAutoSizeValues) {
+                    mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_UNIFORM;
+                    mAutoSizeMinTextSizeInPx = mAutoSizeTextSizesInPx[0];
+                    mAutoSizeMaxTextSizeInPx = mAutoSizeTextSizesInPx[sizesLength - 1];
+                    mAutoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+                } else {
+                    throw new IllegalArgumentException("None of the preset sizes is valid: "
+                        + Arrays.toString(presetSizes));
+                }
+            } else {
+                mHasPresetAutoSizeValues = false;
+            }
+            setupAutoSizeText();
+        }
+    }
+
+    /**
      * Returns the type of auto-size set for this widget.
      *
      * @return an {@code int} corresponding to one of the auto-size types:
@@ -1649,7 +1812,9 @@
      *
      * @attr ref android.R.styleable#TextView_autoSizeText
      *
-     * @see #setAutoSizeTextType(int)
+     * @see #setAutoSizeTextTypeWithDefaults(int)
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
      */
     @AutoSizeTextType
     public int getAutoSizeTextType() {
@@ -1657,155 +1822,53 @@
     }
 
     /**
-     * Sets the auto-size step granularity. It is used in conjunction with auto-size minimum
-     * and maximum text size in order to build the set of text sizes the system uses to choose
-     * from when auto-sizing.
-     *
-     * @param unit the desired dimension unit. See {@link TypedValue} for the possible
-     *             dimension units
-     * @param size the desired size in the given units
+     * @return the current auto-size step granularity in pixels.
      *
      * @attr ref android.R.styleable#TextView_autoSizeStepGranularity
      *
-     * @see #getAutoSizeStepGranularity()
-     * @see #setAutoSizeMinTextSize(int, float)
-     * @see #setAutoSizeMaxTextSize(int, float)
-     */
-    public void setAutoSizeStepGranularity(int unit, float size) {
-        if (supportsAutoSizeText()) {
-            mAutoSizeStepGranularityInPx = (int) TypedValue.applyDimension(
-                    unit, size, getResources().getDisplayMetrics());
-            mHasPresetAutoSizeValues = false;
-            setupAutoSizeText();
-        }
-    }
-
-    /**
-     * @return the current auto-size step granularity in pixels.
-     *
-     * @see #setAutoSizeStepGranularity(int, float)
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
      */
     public int getAutoSizeStepGranularity() {
         return mAutoSizeStepGranularityInPx;
     }
 
     /**
-     * Sets the minimum text size to be used in conjunction with auto-size maximum text size and
-     * auto-size step granularity in order to build the set of text sizes the system uses to choose
-     * from when auto-sizing.
-     *
-     * @param unit the desired dimension unit. See {@link TypedValue} for the possible
-     *             dimension units
-     * @param size the desired size in the given units
+     * @return the current auto-size minimum text size in pixels (the default is 12sp). Note that
+     *         if auto-size has not been configured this function returns {@code -1}.
      *
      * @attr ref android.R.styleable#TextView_autoSizeMinTextSize
      *
-     * @see #getAutoSizeMinTextSize()
-     * @see #setAutoSizeMaxTextSize(int, float)
-     * @see #setAutoSizeStepGranularity(int, float)
-     */
-    public void setAutoSizeMinTextSize(int unit, float size) {
-        if (supportsAutoSizeText()) {
-            mAutoSizeMinTextSizeInPx = (int) TypedValue.applyDimension(
-                    unit, size, getResources().getDisplayMetrics());
-            mHasPresetAutoSizeValues = false;
-            setupAutoSizeText();
-        }
-    }
-
-    /**
-     * @return the current auto-size minimum text size in pixels (the default is 12sp). Note that
-     *         if auto-size has not been configured this function returns {@code 0}.
-     *
-     * @see #setAutoSizeMinTextSize(int, float)
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
      */
     public int getAutoSizeMinTextSize() {
         return mAutoSizeMinTextSizeInPx;
     }
 
     /**
-     * Sets the maximum text size to be used in conjunction with auto-size minimum text size and
-     * auto-size step granularity in order to build the set of text sizes the system uses to choose
-     * from when auto-sizing.
-     *
-     * @param unit the desired dimension unit. See {@link TypedValue} for the possible
-     *             dimension units
-     * @param size the desired size in the given units
+     * @return the current auto-size maximum text size in pixels (the default is 112sp). Note that
+     *         if auto-size has not been configured this function returns {@code -1}.
      *
      * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
      *
-     * @see #getAutoSizeMaxTextSize()
-     * @see #setAutoSizeMinTextSize(int, float)
-     * @see #setAutoSizeStepGranularity(int, float)
-     */
-    public void setAutoSizeMaxTextSize(int unit, float size) {
-        if (supportsAutoSizeText()) {
-            mAutoSizeMaxTextSizeInPx = (int) TypedValue.applyDimension(
-                    unit, size, getResources().getDisplayMetrics());
-            mHasPresetAutoSizeValues = false;
-            setupAutoSizeText();
-        }
-    }
-
-    /**
-     * @return the current auto-size maximum text size in pixels (the default is 112sp). Note that
-     *         if auto-size has not been configured this function returns {@code 0}.
-     *
-     * @see #setAutoSizeMaxTextSize(int, float)
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
      */
     public int getAutoSizeMaxTextSize() {
         return mAutoSizeMaxTextSizeInPx;
     }
 
     /**
-     * Sets a predefined array of sizes to be used when auto-sizing.
-     *
-     * <ul>Note:
-     * <li>when <code>presetSizes</code> is not empty then the auto-size algorithm will use the
-     * values provided here instead of calculating the values based on min, max and step size. Also
-     * the values will be de-duplicated, sorted and negative or zero values will be removed.
-     * <li>when <code>presetSizes</code> is empty then the auto-size algorithm will use the min, max
-     * and step size to build the set of available sizes to choose from. Note that if no values have
-     * been provided for any of min, max or step size then defaults will be used.
-     * </ul>
-     *
-     * @param presetSizes an {@code int} array of sizes in pixels
-     *
-     * @attr ref android.R.styleable#TextView_autoSizePresetSizes
-     *
-     * @see #getAutoSizeTextAvailableSizes()
-     */
-    public void setAutoSizeTextPresetSizes(@NonNull int[] presetSizes) {
-        if (supportsAutoSizeText()) {
-            if (presetSizes.length > 0) {
-                mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(presetSizes);
-                final int sizesLength = mAutoSizeTextSizesInPx.length;
-                mHasPresetAutoSizeValues = sizesLength > 0;
-                if (mHasPresetAutoSizeValues) {
-                    mAutoSizeMinTextSizeInPx = mAutoSizeTextSizesInPx[0];
-                    mAutoSizeMaxTextSizeInPx = mAutoSizeTextSizesInPx[sizesLength - 1];
-                    mAutoSizeStepGranularityInPx = 0;
-                }
-            } else {
-                mHasPresetAutoSizeValues = false;
-            }
-            setupAutoSizeText();
-        }
-    }
-
-    /**
      * @return the current auto-size {@code int} sizes array (in pixels).
      *
-     * @see #setAutoSizeTextPresetSizes(int[])
-     * @see #setAutoSizeMinTextSize(int, float)
-     * @see #setAutoSizeMaxTextSize(int, float)
-     * @see #setAutoSizeStepGranularity(int, float)
+     * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
+     * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
      */
     public int[] getAutoSizeTextAvailableSizes() {
         return mAutoSizeTextSizesInPx;
     }
 
-    private void setupAutoSizePresetSizes(TypedArray textSizes) {
+    private void setupAutoSizeUniformPresetSizes(TypedArray textSizes) {
         final int textSizesLength = textSizes.length();
         final int[] parsedSizes = new int[textSizesLength];
 
@@ -1818,6 +1881,49 @@
         }
     }
 
+    /**
+     * If all params are valid then save the auto-size configuration.
+     *
+     * @throws IllegalArgumentException if any of the params are invalid
+     */
+    private void validateAndSetAutoSizeTextTypeUniformConfiguration(
+            int autoSizeMinTextSizeInPx,
+            int autoSizeMaxTextSizeInPx,
+            int autoSizeStepGranularityInPx) throws IllegalArgumentException {
+        // First validate.
+        if (autoSizeMinTextSizeInPx <= 0) {
+            throw new IllegalArgumentException("Minimum auto-size text size ("
+                + autoSizeMinTextSizeInPx  + "px) is less or equal to 0px)");
+        }
+
+        if (autoSizeMaxTextSizeInPx <= autoSizeMinTextSizeInPx) {
+            throw new IllegalArgumentException("Maximum auto-size text size ("
+                + autoSizeMaxTextSizeInPx + "px) is less or equal to minimum auto-size "
+                + "text size (" + autoSizeMinTextSizeInPx + "px)");
+        }
+
+        if (autoSizeStepGranularityInPx <= 0) {
+            throw new IllegalArgumentException("Minimum auto-size text size ("
+                + autoSizeStepGranularityInPx + "px) is less or equal to 0px)");
+        }
+
+        // All good, persist the configuration.
+        mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_UNIFORM;
+        mAutoSizeMinTextSizeInPx = autoSizeMinTextSizeInPx;
+        mAutoSizeMaxTextSizeInPx = autoSizeMaxTextSizeInPx;
+        mAutoSizeStepGranularityInPx = autoSizeStepGranularityInPx;
+        mHasPresetAutoSizeValues = false;
+    }
+
+    private void clearAutoSizeConfiguration() {
+        mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
+        mAutoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+        mAutoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+        mAutoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
+        mAutoSizeTextSizesInPx = EmptyArray.INT;
+        mNeedsAutoSizeText = false;
+    }
+
     // Returns distinct sorted positive values.
     private int[] cleanupAutoSizePresetSizes(int[] presetValues) {
         final int presetValuesLength = presetValues.length;
@@ -1846,33 +1952,6 @@
             // Calculate the sizes set based on minimum size, maximum size and step size if we do
             // not have a predefined set of sizes or if the current sizes array is empty.
             if (!mHasPresetAutoSizeValues || mAutoSizeTextSizesInPx.length == 0) {
-                // Set valid defaults.
-                if (mAutoSizeMinTextSizeInPx <= 0) {
-                    mAutoSizeMinTextSizeInPx = (int) TypedValue.applyDimension(
-                            TypedValue.COMPLEX_UNIT_SP,
-                            DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP,
-                            getResources().getDisplayMetrics());
-                }
-
-                if (mAutoSizeMaxTextSizeInPx <= 0) {
-                    mAutoSizeMaxTextSizeInPx = (int) TypedValue.applyDimension(
-                            TypedValue.COMPLEX_UNIT_SP,
-                            DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP,
-                            getResources().getDisplayMetrics());
-                }
-
-                if (mAutoSizeStepGranularityInPx <= 0) {
-                    mAutoSizeStepGranularityInPx = DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX;
-                }
-
-                // Validate.
-                if (mAutoSizeMaxTextSizeInPx <= mAutoSizeMinTextSizeInPx) {
-                    throw new IllegalStateException("Maximum auto-size text size ("
-                            + mAutoSizeMaxTextSizeInPx
-                            + "px) is less or equal to minimum auto-size "
-                            + "text size (" + mAutoSizeMinTextSizeInPx + "px)");
-                }
-
                 // Calculate sizes to choose from based on the current auto-size configuration.
                 int autoSizeValuesLength = (int) Math.ceil(
                         (mAutoSizeMaxTextSizeInPx - mAutoSizeMinTextSizeInPx)
@@ -4999,6 +5078,7 @@
 
     private void setText(CharSequence text, BufferType type,
                          boolean notifyBefore, int oldlen) {
+        mTextFromResource = false;
         if (text == null) {
             text = "";
         }
@@ -5233,6 +5313,7 @@
     @android.view.RemotableViewMethod
     public final void setText(@StringRes int resid) {
         setText(getContext().getResources().getText(resid));
+        mTextFromResource = true;
     }
 
     /**
@@ -5259,6 +5340,7 @@
      */
     public final void setText(@StringRes int resid, BufferType type) {
         setText(getContext().getResources().getText(resid), type);
+        mTextFromResource = true;
     }
 
     /**
@@ -7953,9 +8035,9 @@
             TEMP_RECTF.setEmpty();
             TEMP_RECTF.right = maxWidth;
             TEMP_RECTF.bottom = maxHeight;
-            final float textSize = findLargestTextSizeWhichFits(TEMP_RECTF);
-            if (textSize != getTextSize()) {
-                setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, textSize);
+            final float optimalTextSize = findLargestTextSizeWhichFits(TEMP_RECTF);
+            if (optimalTextSize != getTextSize()) {
+                setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, optimalTextSize);
             }
         }
     }
@@ -9804,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_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/layout/autofill_dataset_picker.xml b/core/res/res/layout/autofill_dataset_picker.xml
new file mode 100644
index 0000000..40cce7b
--- /dev/null
+++ b/core/res/res/layout/autofill_dataset_picker.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/list"
+    android:layout_width="wrap_content"
+    android:layout_height="fill_parent"
+    android:divider="?android:attr/listDivider"
+    android:background="#ffffffff">
+</ListView>
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index c4e8e9c..40c9941 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -45,7 +45,7 @@
             android:textColor="?attr/colorAccent"
             android:gravity="center_vertical"
             android:layout_alignParentTop="true"
-            android:layout_alignParentRight="true"
+            android:layout_alignParentEnd="true"
             android:singleLine="true" />
 
         <TextView
@@ -59,7 +59,7 @@
             android:paddingEnd="?attr/dialogPreferredPadding"
             android:paddingTop="8dp"
             android:layout_below="@id/profile_button"
-            android:layout_alignParentLeft="true"
+            android:layout_alignParentStart="true"
             android:paddingBottom="8dp" />
     </RelativeLayout>
 
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 e3156a3..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" />
@@ -2840,6 +2841,7 @@
   <java-symbol type="dimen" name="autofill_fill_item_height" />
   <java-symbol type="dimen" name="autofill_fill_min_margin" />
   <java-symbol type="layout" name="autofill_save"/>
+  <java-symbol type="layout" name="autofill_dataset_picker"/>
   <java-symbol type="id" name="autofill_save_title" />
   <java-symbol type="id" name="autofill_save_no" />
   <java-symbol type="id" name="autofill_save_yes" />
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/SettingsTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
similarity index 98%
rename from core/tests/coretests/src/android/provider/SettingsTest.java
rename to core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 019f837..76331a8 100644
--- a/core/tests/coretests/src/android/provider/SettingsTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -24,8 +24,7 @@
 import static java.lang.reflect.Modifier.isPublic;
 import static java.lang.reflect.Modifier.isStatic;
 
-import android.annotation.TargetApi;
-
+import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -36,11 +35,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-/** Unit test for Settings. */
-@TargetApi(25)
+/** Tests that ensure appropriate settings are backed up. */
 @RunWith(AndroidJUnit4.class)
+@Presubmit
 @SmallTest
-public class SettingsTest {
+public class SettingsBackupTest {
 
     /**
      * The following blacklists contain settings that should *not* be backed up and restored to
@@ -64,7 +63,6 @@
                     Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache
                     Settings.System.POINTER_LOCATION, // backup candidate?
                     Settings.System.RINGTONE_CACHE, // internal cache
-                    Settings.System.SCREEN_BRIGHTNESS_FOR_VR, // bug?
                     Settings.System.SETUP_WIZARD_HAS_RUN, // Only used by SuW
                     Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup?
                     Settings.System.SHOW_TOUCHES, // bug?
@@ -246,6 +244,7 @@
                     Settings.Global.NETWORK_AVOID_BAD_WIFI,
                     Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE,
                     Settings.Global.NETWORK_PREFERENCE,
+                    Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE,
                     Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS,
                     Settings.Global.NETWORK_SCORER_APP,
                     Settings.Global.NETWORK_SCORING_PROVISIONED,
@@ -323,6 +322,7 @@
                     Settings.Global.USE_GOOGLE_MAIL,
                     Settings.Global.VT_IMS_ENABLED,
                     Settings.Global.WAIT_FOR_DEBUGGER,
+                    Settings.Global.WAIT_FOR_NETWORK_TIMEOUT_MS,
                     Settings.Global.WARNING_TEMPERATURE,
                     Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
                     Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
@@ -383,7 +383,6 @@
                  Settings.Secure.ASSIST_STRUCTURE_ENABLED,
                  Settings.Secure.AUTO_FILL_SERVICE,
                  Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
-                 Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
                  Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
                  Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
                  Settings.Secure.BACKUP_AUTO_RESTORE,
@@ -422,6 +421,7 @@
                  Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH,
                  Settings.Secure.MULTI_PRESS_TIMEOUT,
                  Settings.Secure.NFC_PAYMENT_FOREGROUND,
+                 Settings.Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME,
                  Settings.Secure.PACKAGE_VERIFIER_STATE,
                  Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT,
                  Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE,
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/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 7206127..136f17e 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -38,7 +38,7 @@
 
     <bool name="def_bluetooth_on">true</bool>
     <bool name="def_wifi_display_on">false</bool>
-    <bool name="def_install_non_market_apps">false</bool>
+    <bool name="def_install_non_market_apps">true</bool>
     <bool name="def_package_verifier_enable">true</bool>
     <!-- Comma-separated list of location providers.
          Network location is off by default because it requires
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ba657e9..85c153c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1037,7 +1037,7 @@
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
 
         // Ensure the caller can access the setting.
-        enforceSettingReadable(name, SETTINGS_TYPE_SECURE, callingUserId);
+        enforceSettingReadable(name, SETTINGS_TYPE_SECURE, UserHandle.getCallingUserId());
 
         // Determine the owning user as some profile settings are cloned from the parent.
         final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name);
@@ -1233,7 +1233,7 @@
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
 
         // Ensure the caller can access the setting.
-        enforceSettingReadable(name, SETTINGS_TYPE_SYSTEM, callingUserId);
+        enforceSettingReadable(name, SETTINGS_TYPE_SYSTEM, UserHandle.getCallingUserId());
 
         // Determine the owning user as some profile settings are cloned from the parent.
         final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name);
@@ -3174,17 +3174,9 @@
                     // setting through the UI.
                     final SettingsState secureSetting = getSecureSettingsLocked(userId);
                     if (!mUserManager.hasUserRestriction(
-                            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, UserHandle.of(userId))
-                            && secureSetting.getSettingLocked(
-                            Settings.Secure.INSTALL_NON_MARKET_APPS).getValue().equals("0")) {
-
+                            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, UserHandle.of(userId))) {
                         secureSetting.insertSettingLocked(Settings.Secure.INSTALL_NON_MARKET_APPS,
                                 "1", null, true, SettingsState.SYSTEM_PACKAGE_NAME);
-                        // For managed profiles with profile owners, DevicePolicyManagerService
-                        // may want to set the user restriction in this case
-                        secureSetting.insertSettingLocked(
-                                Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, "1", null, true,
-                                SettingsState.SYSTEM_PACKAGE_NAME);
                     }
                     currentVersion = 138;
                 }
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java
index 2dbe142..51e4373 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java
@@ -22,7 +22,6 @@
 
 import android.content.Context;
 import android.content.pm.UserInfo;
-import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -48,7 +47,6 @@
 
     private UserManager mUm;
     private boolean mHasUserRestriction;
-    private boolean mSystemSetUserRestriction;
     private List<UserInfo> mCurrentUsers;
 
     private String waitTillValueChanges(String errorMessage, String oldValue) {
@@ -86,9 +84,6 @@
     public void setUp() {
         mUm = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
         mHasUserRestriction = mUm.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
-        mSystemSetUserRestriction = mUm.getUserRestrictionSource(
-                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle())
-                == UserManager.RESTRICTION_SOURCE_SYSTEM;
         mCurrentUsers = mUm.getUsers();
     }
 
@@ -122,13 +117,6 @@
         String value = getSetting(SETTING_TYPE_SECURE, Settings.Secure.INSTALL_NON_MARKET_APPS);
         assertEquals(value, mHasUserRestriction ? "0" : "1");
 
-        if (mHasUserRestriction && !mSystemSetUserRestriction) {
-            // User restriction set by device policy. This case should be covered in DO/PO related
-            // tests. Pass.
-            Log.w(TAG, "User restriction set by PO/DO. Skipping testValueRespectsUserRestriction");
-            return;
-        }
-
         mUm.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, !mHasUserRestriction);
         value = waitTillValueChanges(
                 "Changing user restriction did not change the value of install_non_market_apps",
@@ -144,8 +132,8 @@
 
     @After
     public void tearDown() {
-        if (!mHasUserRestriction || mSystemSetUserRestriction) {
-            // The test may have modified the user restriction state. Restore it.
+        if (mUm.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)
+                != mHasUserRestriction) {
             mUm.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
                     mHasUserRestriction);
         }
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/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index d789b44..c2c6f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -49,6 +49,9 @@
 
     public QuickQSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
+        if (mFooter != null) {
+            removeView((View) mFooter.getView());
+        }
         if (mTileLayout != null) {
             for (int i = 0; i < mRecords.size(); i++) {
                 mTileLayout.removeTile(mRecords.get(i));
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/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 9a4b45a..2d47c7b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -21,6 +21,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.app.admin.DevicePolicyManager;
 import android.app.Notification;
 import android.app.Notification.BigPictureStyle;
 import android.app.NotificationManager;
@@ -46,6 +47,7 @@
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Process;
+import android.os.UserHandle;
 import android.provider.MediaStore;
 import android.util.DisplayMetrics;
 import android.view.Display;
@@ -866,6 +868,16 @@
             .setAutoCancel(true)
             .setColor(context.getColor(
                         com.android.internal.R.color.system_notification_accent_color));
+        final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        final Intent intent = dpm.createAdminSupportIntent(
+                DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
+        if (intent != null) {
+            final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
+                    context, 0, intent, 0, null, UserHandle.CURRENT);
+            b.setContentIntent(pendingIntent);
+        }
+
         SystemUI.overrideNotificationAppName(context, b);
 
         Notification n = new Notification.BigTextStyle(b)
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/packages/WallpaperCropper/Android.mk b/packages/WallpaperCropper/Android.mk
index d8fb7a4..09b41fd 100644
--- a/packages/WallpaperCropper/Android.mk
+++ b/packages/WallpaperCropper/Android.mk
@@ -6,7 +6,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := telephony-common
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 junit
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
 
 LOCAL_PACKAGE_NAME := WallpaperCropper
 LOCAL_CERTIFICATE := platform
diff --git a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/BitmapTexture.java b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/BitmapTexture.java
index 100b0b3b..f8b01cb 100644
--- a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/BitmapTexture.java
+++ b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/BitmapTexture.java
@@ -18,7 +18,7 @@
 
 import android.graphics.Bitmap;
 
-import junit.framework.Assert;
+import com.android.gallery3d.common.Utils;
 
 // BitmapTexture is a texture whose content is specified by a fixed Bitmap.
 //
@@ -34,7 +34,7 @@
 
     public BitmapTexture(Bitmap bitmap, boolean hasBorder) {
         super(hasBorder);
-        Assert.assertTrue(bitmap != null && !bitmap.isRecycled());
+        Utils.assertTrue(bitmap != null && !bitmap.isRecycled());
         mContentBitmap = bitmap;
     }
 
diff --git a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLPaint.java b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLPaint.java
index 16b2206..b26e9ab 100644
--- a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLPaint.java
+++ b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLPaint.java
@@ -16,7 +16,7 @@
 
 package com.android.gallery3d.glrenderer;
 
-import junit.framework.Assert;
+import com.android.gallery3d.common.Utils;
 
 public class GLPaint {
     private float mLineWidth = 1f;
@@ -31,7 +31,7 @@
     }
 
     public void setLineWidth(float width) {
-        Assert.assertTrue(width >= 0);
+        Utils.assertTrue(width >= 0);
         mLineWidth = width;
     }
 
diff --git a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/UploadedTexture.java b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/UploadedTexture.java
index f41a979..417102a 100644
--- a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/UploadedTexture.java
+++ b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/UploadedTexture.java
@@ -20,7 +20,7 @@
 import android.graphics.Bitmap.Config;
 import android.opengl.GLUtils;
 
-import junit.framework.Assert;
+import com.android.gallery3d.common.Utils;
 
 import java.util.HashMap;
 
@@ -144,7 +144,7 @@
     }
 
     private void freeBitmap() {
-        Assert.assertTrue(mBitmap != null);
+        Utils.assertTrue(mBitmap != null);
         onFreeBitmap(mBitmap);
         mBitmap = null;
     }
@@ -219,7 +219,7 @@
                 int texWidth = getTextureWidth();
                 int texHeight = getTextureHeight();
 
-                Assert.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
+                Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
 
                 // Upload the bitmap to a new texture.
                 mId = canvas.getGLId().generateTexture();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index d1042b8..44afe1d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2006,23 +2006,26 @@
     private void updateFingerprintGestureHandling(UserState userState) {
         final List<Service> services;
         synchronized (mLock) {
-            // Only create the controller when a service wants to use the feature
             services = userState.mBoundServices;
-            int numServices = services.size();
-            for (int i = 0; i < numServices; i++) {
-                if (services.get(i).isCapturingFingerprintGestures()) {
-                    final long identity = Binder.clearCallingIdentity();
-                    IFingerprintService service = null;
-                    try {
-                        service = IFingerprintService.Stub.asInterface(
-                                ServiceManager.getService(Context.FINGERPRINT_SERVICE));
-                    } finally {
-                        Binder.restoreCallingIdentity(identity);
-                    }
-                    if (service != null) {
-                        mFingerprintGestureDispatcher = new FingerprintGestureDispatcher(
-                                service, mLock);
-                        break;
+            if ((mFingerprintGestureDispatcher == null)
+                    &&  mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+                // Only create the controller when a service wants to use the feature
+                int numServices = services.size();
+                for (int i = 0; i < numServices; i++) {
+                    if (services.get(i).isCapturingFingerprintGestures()) {
+                        final long identity = Binder.clearCallingIdentity();
+                        IFingerprintService service = null;
+                        try {
+                            service = IFingerprintService.Stub.asInterface(
+                                    ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+                        } finally {
+                            Binder.restoreCallingIdentity(identity);
+                        }
+                        if (service != null) {
+                            mFingerprintGestureDispatcher = new FingerprintGestureDispatcher(
+                                    service, mLock);
+                            break;
+                        }
                     }
                 }
             }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index a697a8e..081acf5 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -451,7 +451,7 @@
              * Called when the fill UI is ready to be shown for this view.
              */
             void onFillReady(ViewState viewState, FillResponse fillResponse, Rect bounds,
-                    @Nullable AutoFillValue value);
+                    AutoFillId focusedId, @Nullable AutoFillValue value);
         }
 
         final AutoFillId mId;
@@ -509,12 +509,14 @@
         }
 
         /**
-         * Calls {@link Listener#onFillReady(ViewState, FillResponse, Rect, AutoFillValue)} if the
+         * Calls {@link
+         * Listener#onFillReady(ViewState, FillResponse, Rect, AutoFillId, AutoFillValue)} if the
          * fill UI is ready to be displayed (i.e. when response and bounds are set).
          */
         void maybeCallOnFillReady() {
-            if (mResponse != null && mBounds != null) {
-                mListener.onFillReady(this, mResponse, mBounds, mAutoFillValue);
+            if (mResponse != null && (mResponse.getAuthentication() != null
+                    || mResponse.getDatasets() != null) && mBounds != null) {
+                mListener.onFillReady(this, mResponse, mBounds, mId, mAutoFillValue);
             }
         }
 
@@ -641,8 +643,14 @@
 
         // FillServiceCallbacks
         @Override
-        public void authenticate(IntentSender intent, Intent fillInIntent) {
-            startAuthentication(intent, fillInIntent);
+        public void authenticate(IntentSender intent) {
+            final Intent fillInIntent;
+            synchronized (mLock) {
+                fillInIntent = createAuthFillInIntent(mStructure);
+            }
+            mHandlerCaller.getHandler().post(() -> {
+                startAuthentication(intent, fillInIntent);
+            });
         }
 
         // FillServiceCallbacks
@@ -675,15 +683,10 @@
                     processResponseLocked(mCurrentResponse);
                 } else if (result instanceof Dataset) {
                     Dataset dataset = (Dataset) result;
-                    final int datasetIndex = Helper.indexOfDataset(
-                            dataset.getName(), mCurrentResponse);
-                    if (datasetIndex <= 0) {
-                        Slog.e(TAG, "Response for a dataset auth has"
-                                + " an invalid dataset result: " + dataset.getName());
-                    }
-                    mCurrentResponse.getDatasets().removeAt(datasetIndex);
+                    mCurrentResponse.getDatasets().remove(mAutoFilledDataset);
                     mCurrentResponse.getDatasets().add(dataset);
-                    autoFill(dataset);
+                    mAutoFilledDataset = dataset;
+                    processResponseLocked(mCurrentResponse);
                 }
             }
         }
@@ -727,6 +730,7 @@
                     return;
                 }
             }
+
             // Nothing changed...
             if (DEBUG) Slog.d(TAG, "showSaveLocked(): with no changes, comes no responsibilities");
         }
@@ -802,8 +806,11 @@
                     }
                 }
 
-                // Just change value, don't update the UI
+                // Change value
                 viewState.mAutoFillValue = value;
+
+                // Update the chooser UI
+                mUi.updateFillUi(value.coerceToString());
                 return;
             }
 
@@ -838,7 +845,7 @@
 
         @Override
         public void onFillReady(ViewState viewState, FillResponse response, Rect bounds,
-                @Nullable AutoFillValue value) {
+                AutoFillId filledId, @Nullable AutoFillValue value) {
             String filterText = "";
             if (value != null) {
                 // TODO(b/33197203): Handle other AutoFillValue types
@@ -848,8 +855,7 @@
                 }
             }
 
-            getUiForShowing().showFillUi(mActivityToken, viewState, response.getDatasets(),
-                    bounds, filterText);
+            getUiForShowing().showFillUi(filledId, response, bounds, filterText);
         }
 
         private void processResponseLocked(FillResponse response) {
@@ -869,26 +875,17 @@
             if (mCurrentResponse.getAuthentication() != null) {
                 // Handle authentication.
                 final Intent fillInIntent = createAuthFillInIntent(mStructure);
-
                 mCurrentViewState.setResponse(mCurrentResponse, fillInIntent);
                 return;
             }
 
-            final ArraySet<AutoFillId> savableIds = mCurrentResponse.getSavableIds();
-            if (savableIds == null || savableIds.isEmpty()) {
-                // NOTE: it's assuming the response has no datasets, since when a dataset is added
-                // it's view id is automatically added to savable_ids
-                if (DEBUG) Slog.d(TAG, "processResponseLocked(): nothing to do");
-
-                removeSelf();
-                return;
-            }
-
             mCurrentViewState.setResponse(mCurrentResponse);
         }
 
         void autoFill(Dataset dataset) {
             synchronized (mLock) {
+                mAutoFilledDataset = dataset;
+
                 // Autofill it directly...
                 if (dataset.getAuthentication() == null) {
                     autoFillApp(dataset);
@@ -948,9 +945,7 @@
             synchronized (mLock) {
                 try {
                     if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
-
                     mClient.autoFill(dataset.getFieldIds(), dataset.getFieldValues());
-                    mAutoFilledDataset = dataset;
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Error auto-filling activity: " + e);
                 }
@@ -999,7 +994,6 @@
 
         private void destroyLocked() {
             mRemoteFillService.destroy();
-            mUi.hideAll();
             mUi.setCallbackLocked(null, null);
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
index e83dc1e..78dd247 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
@@ -18,32 +18,23 @@
 import static com.android.server.autofill.Helper.DEBUG;
 
 import android.annotation.Nullable;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.graphics.Rect;
-import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
 import android.service.autofill.Dataset;
-import android.util.ArraySet;
-import android.os.Looper;
+import android.service.autofill.FillResponse;
 import android.text.format.DateUtils;
 import android.util.Slog;
 import android.view.Gravity;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.view.autofill.AutoFillId;
 import android.widget.Toast;
 
-import com.android.internal.os.HandlerCaller;
 import com.android.server.UiThread;
-import com.android.server.autofill.AutoFillManagerServiceImpl.ViewState;
 
 import java.io.PrintWriter;
 
@@ -53,36 +44,24 @@
 // TODO(b/33197203): document exactly what once the auto-fill bar is implemented
 final class AutoFillUI {
     private static final String TAG = "AutoFillUI";
-    private static final long SNACK_BAR_LIFETIME_MS = 30 * DateUtils.SECOND_IN_MILLIS;
+
+    private static final long SNACK_BAR_LIFETIME_MS = 5 * DateUtils.SECOND_IN_MILLIS;
+
     private static final int MSG_HIDE_SNACK_BAR = 1;
 
+    private final Handler mHandler = UiThread.getHandler();
+
     private final Context mContext;
+
     private final WindowManager mWm;
 
-    // TODO(b/33197203) Fix locking - some state requires lock and some not - requires refactoring
-    private final Object mLock = new Object();
-
-    // Fill UI variables
     private AnchoredWindow mFillWindow;
-    private View mFillView;
-    private ViewState mViewState;
+
+    private DatasetPicker mDatasetPicker;
 
     private AutoFillUiCallback mCallback;
-    private IBinder mActivityToken;
 
-    private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
-        switch (msg.what) {
-            case MSG_HIDE_SNACK_BAR: {
-                hideSnackbarUiThread();
-                return;
-            }
-            default: {
-                Slog.w(TAG, "Invalid message: " + msg);
-            }
-        }
-    };
-    private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(),
-            mHandlerCallback, true);
+    private IBinder mActivityToken;
 
     /**
      * Custom snackbar UI used for saving autofill or other informational messages.
@@ -95,32 +74,32 @@
     }
 
     void setCallbackLocked(AutoFillUiCallback callback, IBinder activityToken) {
-        hideAll();
-        mCallback = callback;
-        mActivityToken = activityToken;
+        mHandler.post(() -> {
+            hideAllUiThread();
+            mCallback = callback;
+            mActivityToken = activityToken;
+        });
     }
 
     /**
      * Displays an error message to the user.
      */
     void showError(CharSequence message) {
-        if (!hasCallback()) {
-            return;
-        }
-        hideAll();
         // TODO(b/33197203): proper implementation
-        UiThread.getHandler().runWithScissors(() -> {
+        UiThread.getHandler().post(() -> {
+            if (!hasCallback()) {
+                return;
+            }
+            hideAllUiThread();
             Toast.makeText(mContext, "AutoFill error: " + message, Toast.LENGTH_LONG).show();
-        }, 0);
+        });
     }
 
     /**
      * Hides the fill UI.
      */
     void hideFillUi() {
-        UiThread.getHandler().runWithScissors(() -> {
-            hideFillUiUiThread();
-        }, 0);
+        mHandler.post(() -> hideFillUiUiThread());
     }
 
     @android.annotation.UiThread
@@ -129,111 +108,80 @@
             if (DEBUG) Slog.d(TAG, "hideFillUiUiThread(): hide" + mFillWindow);
             mFillWindow.hide();
         }
-
-        mViewState = null;
-        mFillView = null;
         mFillWindow = null;
+        mDatasetPicker = null;
+    }
+
+    void updateFillUi(@Nullable String filterText) {
+        mHandler.post(() -> {
+            if (!hasCallback()) {
+                return;
+            }
+            hideSnackbarUiThread();
+            if (mDatasetPicker != null) {
+                mDatasetPicker.update(filterText);
+            }
+        });
     }
 
     /**
      * Shows the fill UI, removing the previous fill UI if the has changed.
      *
-     * @param appToken the token of the app to be autofilled
-     * @param viewState the view state, compared by reference to know if new UI should be shown
-     * @param datasets the datasets to show, not used if viewState is the same
+     * @param focusedId the currently focused field
+     * @param response the current fill response
      * @param bounds bounds of the view to be filled, used if changed
      * @param filterText text of the view to be filled, used if changed
      */
-    void showFillUi(IBinder appToken, ViewState viewState, @Nullable ArraySet<Dataset> datasets,
-            Rect bounds, String filterText) {
-        if (!hasCallback()) {
-            return;
-        }
-
-        UiThread.getHandler().runWithScissors(() -> {
+    void showFillUi(AutoFillId focusedId, @Nullable FillResponse response, Rect bounds,
+            String filterText) {
+        mHandler.post(() -> {
+            if (!hasCallback()) {
+                return;
+            }
             hideSnackbarUiThread();
-        }, 0);
-
-        if (datasets == null && viewState.mAuthIntent == null) {
-            // TODO(b/33197203): shouldn't be called, but keeping the WTF for a while just to be
-            // safe, otherwise it would crash system server...
-            Slog.wtf(TAG, "showFillUI(): no dataset");
-            return;
-        }
-
-        // TODO(b/33197203): should not display UI after we launched an authentication intent, since
-        // we have no warranty the provider will call onFailure() if the authentication failed or
-        // user dismissed the auth window
-        // because if the service does not handle calling the callback,
-
-
-        UiThread.getHandler().runWithScissors(() -> {
-            // The dataset picker is only shown when authentication is not required...
-            DatasetPicker datasetPicker = null;
-
-            if (mViewState == null || !mViewState.mId.equals(viewState.mId)) {
-                hideFillUiUiThread();
-
-                mViewState = viewState;
-                if (viewState.mAuthIntent != null) {
-                    final CharSequence serviceName = viewState.getServiceName();
-
-                    mFillView = new SignInPrompt(mContext, serviceName, (e) -> {
-                        final IntentSender intentSender = viewState.mResponse.getAuthentication();
-                        final AutoFillUiCallback callback;
-                        final Intent authIntent;
-                        synchronized (mLock) {
-                            callback = mCallback;
-                            authIntent = viewState.mAuthIntent;
-                            // Must reset the authentication intent so UI display the datasets after
-                            // the user authenticated.
-                            viewState.mAuthIntent = null;
+            final View content;
+            if (response.getPresentation() != null) {
+                content = response.getPresentation().apply(mContext, null);
+                content.setOnClickListener((view) -> {
+                    if (mCallback != null) {
+                        mCallback.authenticate(response.getAuthentication());
+                    }
+                    hideFillUiUiThread();
+                });
+            } else {
+                mDatasetPicker = new DatasetPicker(mContext, response.getDatasets(),
+                        focusedId, new DatasetPicker.Listener() {
+                    @Override
+                    public void onDatasetPicked(Dataset dataset) {
+                        if (mCallback != null) {
+                            mCallback.fill(dataset);
                         }
-                        if (callback != null) {
-                            callback.authenticate(intentSender, authIntent);
-                        } else {
-                            // TODO(b/33197203): need to figure out why it's null sometimes
-                            Slog.w(TAG, "no callback on showFillUi().auth for " + viewState.mId);
-                        }
-                    });
+                        hideFillUiUiThread();
+                    }
 
-                } else {
-                    mFillView = datasetPicker = new DatasetPicker(mContext, datasets,
-                            (dataset) -> {
-                                final AutoFillUiCallback callback;
-                                synchronized (mLock) {
-                                    callback = mCallback;
-                                }
-                                if (callback != null) {
-                                    callback.fill(dataset);
-                                } else {
-                                    // TODO(b/33197203): need to figure out why it's null sometimes
-                                    Slog.w(TAG, "no callback on showFillUi() for " + viewState.mId);
-                                }
-                                hideFillUiUiThread();
-                            });
-                }
-                mFillWindow = new AnchoredWindow(mWm, appToken, mFillView);
+                    @Override
+                    public void onCanceled() {
+                        hideFillUiUiThread();
+                    }
+                });
+                mDatasetPicker.update(filterText);
+                content = mDatasetPicker;
+            }
 
-                if (DEBUG) Slog.d(TAG, "showFillUi(): view changed for: " + viewState.mId);
-            }
-            if (datasetPicker != null) {
-                datasetPicker.update(filterText);
-            }
+            mFillWindow = new AnchoredWindow(mWm, mActivityToken, content);
             mFillWindow.show(bounds);
-
-        }, 0);
+        });
     }
 
     /**
      * Shows the UI asking the user to save for auto-fill.
      */
     void showSaveUi() {
-        if (!hasCallback()) {
-            return;
-        }
-        hideAll();
-        UiThread.getHandler().runWithScissors(() -> {
+        mHandler.post(() -> {
+            if (!hasCallback()) {
+                return;
+            }
+            hideAllUiThread();
             showSnackbarUiThread(new SavePrompt(mContext,
                     new SavePrompt.OnSaveListener() {
                 @Override
@@ -249,17 +197,20 @@
                     hideSnackbarUiThread();
                 }
             }));
-        }, 0);
+        });
     }
 
     /**
      * Hides all UI affordances.
      */
     void hideAll() {
-        UiThread.getHandler().runWithScissors(() -> {
-            hideSnackbarUiThread();
-            hideFillUiUiThread();
-        }, 0);
+        mHandler.post(() -> hideAllUiThread());
+    }
+
+    @android.annotation.UiThread
+    private void hideAllUiThread() {
+        hideSnackbarUiThread();
+        hideFillUiUiThread();
     }
 
     void dump(PrintWriter pw) {
@@ -267,14 +218,13 @@
         final String prefix = "  ";
         pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
         pw.print(prefix); pw.print("mSnackBar: "); pw.println(mSnackbar);
-        pw.print(prefix); pw.print("mViewState: "); pw.println(mViewState);
     }
 
     //similar to a snackbar, but can be a bit custom since it is more than just text. This will
     //allow two buttons for saving or not saving the autofill for instance as well.
+    @android.annotation.UiThread
     private void showSnackbarUiThread(View snackBar) {
         final LayoutParams params = new LayoutParams();
-        params.setTitle("AutoFill Save");
         params.type = LayoutParams.TYPE_PHONE; // TODO(b/33197203) use app window token
         params.flags =
                 LayoutParams.FLAG_NOT_FOCUSABLE // don't receive input events,
@@ -286,20 +236,21 @@
         params.width = LayoutParams.MATCH_PARENT;
         params.height = LayoutParams.WRAP_CONTENT;
 
-        UiThread.getHandler().runWithScissors(() -> {
+        mHandler.post(() -> {
             mSnackbar = snackBar;
             mWm.addView(mSnackbar, params);
-        }, 0);
+        });
 
         if (DEBUG) {
             Slog.d(TAG, "showSnackbar(): auto dismissing it in " + SNACK_BAR_LIFETIME_MS + " ms");
         }
-        mHandlerCaller.sendMessageDelayed(mHandlerCaller.obtainMessage(MSG_HIDE_SNACK_BAR),
-                SNACK_BAR_LIFETIME_MS);
+        mHandler.sendMessageDelayed(mHandler
+                        .obtainMessage(MSG_HIDE_SNACK_BAR), SNACK_BAR_LIFETIME_MS);
     }
 
+    @android.annotation.UiThread
     private void hideSnackbarUiThread() {
-        mHandlerCaller.getHandler().removeMessages(MSG_HIDE_SNACK_BAR);
+        mHandler.removeMessages(MSG_HIDE_SNACK_BAR);
         if (mSnackbar != null) {
             mWm.removeView(mSnackbar);
             mSnackbar = null;
@@ -307,13 +258,11 @@
     }
 
     private boolean hasCallback() {
-        synchronized (mLock) {
-            return mCallback != null;
-        }
+        return mCallback != null;
     }
 
     interface AutoFillUiCallback {
-        void authenticate(IntentSender intent, Intent fillInIntent);
+        void authenticate(IntentSender intent);
         void fill(Dataset dataset);
         void save();
     }
diff --git a/services/autofill/java/com/android/server/autofill/DatasetPicker.java b/services/autofill/java/com/android/server/autofill/DatasetPicker.java
index a54cab9..3190f60 100644
--- a/services/autofill/java/com/android/server/autofill/DatasetPicker.java
+++ b/services/autofill/java/com/android/server/autofill/DatasetPicker.java
@@ -16,73 +16,72 @@
 package com.android.server.autofill;
 
 import android.content.Context;
-import android.graphics.Color;
 import android.service.autofill.Dataset;
-import android.text.TextUtils;
 import android.util.ArraySet;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillValue;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ArrayAdapter;
-import android.widget.Filter.FilterListener;
+import android.widget.FrameLayout;
 import android.widget.ListView;
-import android.widget.TextView;
+import android.widget.RemoteViews;
+import com.android.internal.R;
+import com.android.internal.R;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * View for dataset picker.
- *
- * <p>A fill session starts when a View is clicked and FillResponse is supplied.
- * <p>A fill session ends when 1) the user takes action in the UI, 2) another View is clicked, or
- * 3) the View is detached.
+ * This class manages the dataset selection UI.
  */
-final class DatasetPicker extends ListView implements OnItemClickListener {
+final class DatasetPicker extends FrameLayout implements OnItemClickListener {
     interface Listener {
         void onDatasetPicked(Dataset dataset);
+        void onCanceled();
     }
 
     private final Listener mListener;
 
-    DatasetPicker(Context context, ArraySet<Dataset> datasets, Listener listener) {
+    private final ArrayAdapter<ViewItem> mAdapter;
+
+    DatasetPicker(Context context, ArraySet<Dataset> datasets, AutoFillId filteredViewId,
+            Listener listener) {
         super(context);
         mListener = listener;
 
         final List<ViewItem> items = new ArrayList<>(datasets.size());
         for (Dataset dataset : datasets) {
-            items.add(new ViewItem(dataset));
+            final int index = dataset.getFieldIds().indexOf(filteredViewId);
+            if (index >= 0) {
+                AutoFillValue value = dataset.getFieldValues().get(index);
+                items.add(new ViewItem(dataset, value.coerceToString()));
+            }
         }
 
-        final ArrayAdapter<ViewItem> adapter = new ArrayAdapter<ViewItem>(
-            context,
-            android.R.layout.simple_list_item_1,
-            android.R.id.text1,
-            items) {
+        mAdapter = new ArrayAdapter<ViewItem>(context, 0, items) {
             @Override
             public View getView(int position, View convertView, ViewGroup parent) {
-                final TextView textView = (TextView) super.getView(position, convertView, parent);
-                textView.setSingleLine();
-                textView.setEllipsize(TextUtils.TruncateAt.END);
-                textView.setMinHeight(
-                        getDimen(com.android.internal.R.dimen.autofill_fill_item_height));
-                return textView;
+                RemoteViews presentation = getItem(position).getDataset().getPresentation();
+                return presentation.apply(context, parent);
             }
         };
-        setAdapter(adapter);
-        setBackgroundColor(Color.WHITE);
-        setDivider(null);
-        setElevation(getDimen(com.android.internal.R.dimen.autofill_fill_elevation));
-        setOnItemClickListener(this);
+
+        LayoutInflater inflater = LayoutInflater.from(context);
+        ListView content = (ListView) inflater.inflate(
+                com.android.internal.R.layout.autofill_dataset_picker, this, true)
+                .findViewById(com.android.internal.R.id.list);
+        content.setAdapter(mAdapter);
+        content.setOnItemClickListener(this);
     }
 
     public void update(String prefix) {
-        final ArrayAdapter<ViewItem> adapter = (ArrayAdapter) getAdapter();
-        adapter.getFilter().filter(prefix, new FilterListener() {
-            @Override
-            public void onFilterComplete(int count) {
-                setVisibility(count > 0 ? View.VISIBLE : View.GONE);
+        mAdapter.getFilter().filter(prefix, (count) -> {
+            if (count <= 0 && mListener != null) {
+                mListener.onCanceled();
             }
         });
     }
@@ -91,29 +90,27 @@
     public void onItemClick(AdapterView<?> adapterView, View view, int pos, long id) {
         if (mListener != null) {
             final ViewItem vi = (ViewItem) adapterView.getItemAtPosition(pos);
-            mListener.onDatasetPicked(vi.getData());
+            mListener.onDatasetPicked(vi.getDataset());
         }
     }
 
-    private int getDimen(int resId) {
-        return getContext().getResources().getDimensionPixelSize(resId);
-    }
-
     private static class ViewItem {
-        private final Dataset mData;
+        private final String mValue;
+        private final Dataset mDataset;
 
-        ViewItem(Dataset data) {
-            mData = data;
+        ViewItem(Dataset dataset, String value) {
+            mDataset = dataset;
+            mValue = value;
         }
 
-        public Dataset getData() {
-            return mData;
+        public Dataset getDataset() {
+            return mDataset;
         }
 
         @Override
         public String toString() {
             // used by ArrayAdapter
-            return mData.getName().toString();
+            return mValue;
         }
     }
 }
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 48ae635..2f600c2 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -19,8 +19,6 @@
 import android.annotation.Nullable;
 import android.os.Bundle;
 import android.service.autofill.Dataset;
-import android.service.autofill.FillResponse;
-import android.util.ArraySet;
 import android.view.autofill.AutoFillId;
 import android.view.autofill.AutoFillValue;
 
@@ -77,28 +75,6 @@
         return null;
     }
 
-    /**
-     * Finds the index of a data set given its name.
-     *
-     * @param name The dataset name.
-     * @param response The response to search.
-     * @return The index of dataset if found or -1.
-     */
-    static int indexOfDataset(CharSequence name, FillResponse response) {
-        ArraySet<Dataset> datasets = response.getDatasets();
-        if (datasets == null || datasets.isEmpty()) {
-            return -1;
-        }
-        final int datasetCount = datasets.size();
-        for (int i = 0; i < datasetCount; i++) {
-            Dataset dataset = datasets.valueAt(i);
-            if (dataset.getName().toString().equals(name.toString())) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
     private Helper() {
         throw new UnsupportedOperationException("contains static members only");
     }
diff --git a/services/autofill/java/com/android/server/autofill/SavePrompt.java b/services/autofill/java/com/android/server/autofill/SavePrompt.java
index f0b51e2..85c6d7d 100644
--- a/services/autofill/java/com/android/server/autofill/SavePrompt.java
+++ b/services/autofill/java/com/android/server/autofill/SavePrompt.java
@@ -17,9 +17,7 @@
 package com.android.server.autofill;
 
 import android.content.Context;
-import android.graphics.Color;
 import android.widget.RelativeLayout;
-import android.widget.RelativeLayout.LayoutParams;
 import android.widget.TextView;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -55,6 +53,5 @@
             mListener.onSaveClick();
         });
 
-        //addView(view);
     }
 }
diff --git a/services/autofill/java/com/android/server/autofill/SignInPrompt.java b/services/autofill/java/com/android/server/autofill/SignInPrompt.java
deleted file mode 100644
index 6d17acd..0000000
--- a/services/autofill/java/com/android/server/autofill/SignInPrompt.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.autofill;
-
-import android.content.Context;
-import android.view.View;
-import android.widget.Button;
-
-/**
- * A view displaying the sign-in prompt for an auto-fill service.
- */
-final class SignInPrompt extends Button {
-
-    SignInPrompt(Context context, CharSequence serviceName, View.OnClickListener listener) {
-        super(context);
-        // TODO(b/33197203): use strings.xml
-        final String text = serviceName != null
-                ? "Sign in to " + serviceName + " to autofill"
-                : "Sign in to autofill";
-
-        // TODO(b/33197203): polish UI / use better altenative than a button...
-        setText(text);
-        setOnClickListener(listener);
-    }
-}
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/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 8442c11..fe5b3a2 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -2046,7 +2046,7 @@
         }
         mWindowManagerInternal.updateInputMethodWindowStatus(token,
                 (vis & InputMethodService.IME_VISIBLE) != 0,
-                token != null ? info.mTargetWindow : null);
+                info != null ? info.mTargetWindow : null);
     }
 
     private void updateSystemUi(IBinder token, int vis, int backDisposition) {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 4a44530..f76ddc7 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -1064,6 +1064,14 @@
 
     private void setLockCredentialInternal(String credential, int credentialType,
             String savedCredential, int userId) throws RemoteException {
+        // Normalize savedCredential and credential such that empty string is always represented
+        // as null.
+        if (TextUtils.isEmpty(savedCredential)) {
+            savedCredential = null;
+        }
+        if (TextUtils.isEmpty(credential)) {
+            credential = null;
+        }
         synchronized (mSpManager) {
             if (isSyntheticPasswordBasedCredentialLocked(userId)) {
                 spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index c77a407..33351ff 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -20,9 +20,12 @@
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.BatteryManager;
-import android.os.BatteryManagerInternal;
+import android.os.BatteryProperties;
 import android.os.Build;
+import android.os.IBatteryPropertiesListener;
+import android.os.IBatteryPropertiesRegistrar;
 import android.os.RecoverySystem;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -31,11 +34,15 @@
 import android.text.format.DateUtils;
 import android.util.ExceptionUtils;
 import android.util.MathUtils;
+import android.util.MutableBoolean;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.util.ArrayUtils;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Utilities to help rescue the system from crash loops. Callers are expected to
  * report boot events and persistent app crashes, and if they happen frequently
@@ -66,24 +73,26 @@
 
     private static boolean isDisabled() {
         // We're disabled on all engineering devices
-        if (Build.IS_ENG) return true;
+        if (Build.IS_ENG) {
+            Slog.v(TAG, "Disabled because of eng build");
+            return true;
+        }
 
         // We're disabled on userdebug devices connected over USB, since that's
         // a decent signal that someone is actively trying to debug the device,
         // or that it's in a lab environment.
-        if (Build.IS_USERDEBUG) {
-            try {
-                if (LocalServices.getService(BatteryManagerInternal.class)
-                        .getPlugType() == BatteryManager.BATTERY_PLUGGED_USB) {
-                    return true;
-                } else {
-                }
-            } catch (Throwable ignored) {
-            }
+        if (Build.IS_USERDEBUG && isUsbActive()) {
+            Slog.v(TAG, "Disabled because of active USB connection");
+            return true;
         }
 
         // One last-ditch check
-        return SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false);
+        if (SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false)) {
+            Slog.v(TAG, "Disabled because of manual property");
+            return true;
+        }
+
+        return false;
     }
 
     /**
@@ -185,14 +194,14 @@
         final ContentResolver resolver = context.getContentResolver();
         try {
             Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM);
-        } catch (Exception e) {
-            res = new RuntimeException("Failed to reset global settings", e);
+        } catch (Throwable t) {
+            res = new RuntimeException("Failed to reset global settings", t);
         }
         for (int userId : getAllUserIds(context)) {
             try {
                 Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId);
-            } catch (Exception e) {
-                res = new RuntimeException("Failed to reset secure settings for " + userId, e);
+            } catch (Throwable t) {
+                res = new RuntimeException("Failed to reset secure settings for " + userId, t);
             }
         }
         if (res != null) {
@@ -314,6 +323,38 @@
         return userIds;
     }
 
+    /**
+     * Hacky test to check if the device has an active USB connection, which is
+     * a good proxy for someone doing local development work. It uses a low
+     * level call since we may not have started {@link BatteryManager} yet.
+     */
+    private static boolean isUsbActive() {
+        final MutableBoolean res = new MutableBoolean(false);
+        final CountDownLatch latch = new CountDownLatch(1);
+        final IBatteryPropertiesListener listener = new IBatteryPropertiesListener.Stub() {
+            @Override
+            public void batteryPropertiesChanged(BatteryProperties props) {
+                res.value = props.chargerUsbOnline;
+                latch.countDown();
+            }
+        };
+
+        try {
+            final IBatteryPropertiesRegistrar bpr = IBatteryPropertiesRegistrar.Stub
+                    .asInterface(ServiceManager.getService("batteryproperties"));
+            bpr.registerListener(listener);
+            try {
+                latch.await(5, TimeUnit.SECONDS);
+            } finally {
+                bpr.unregisterListener(listener);
+            }
+            return res.value;
+        } catch (Throwable t) {
+            Slog.w(TAG, "Failed to determine if device was on USB", t);
+            return false;
+        }
+    }
+
     private static String levelToString(int level) {
         switch (level) {
             case LEVEL_NONE: return "NONE";
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/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 3c109ac..f49ad82 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();
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 185c778..1f64b65 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -106,7 +106,9 @@
         synchronized(mPlayerLock) {
             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
             // FIXME SoundPool not ready for state reporting
-            if (apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+            if (apc != null
+                    && apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL)
+            {
                 return;
             }
             if (checkConfigurationCaller(piid, apc, binderUid)) {
@@ -278,6 +280,9 @@
             while (piidIterator.hasNext()) {
                 final Integer piid = piidIterator.next();
                 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
+                if (apc == null) {
+                    continue;
+                }
                 if (!winner.hasSameUid(apc.getClientUid())
                         && loser.hasSameUid(apc.getClientUid())
                         && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED)
@@ -363,6 +368,9 @@
             while (piidIterator.hasNext()) {
                 final Integer piid = piidIterator.next();
                 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
+                if (apc == null) {
+                    continue;
+                }
                 final int playerUsage = apc.getAudioAttributes().getUsage();
                 boolean mute = false;
                 for (int usageToMute : usagesToMute) {
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/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 407262b..36f3287 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -64,6 +64,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.KeyEvent;
+import android.view.ViewConfiguration;
 
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -99,6 +100,7 @@
     private final Object mLock = new Object();
     private final MessageHandler mHandler = new MessageHandler();
     private final PowerManager.WakeLock mMediaEventWakeLock;
+    private final int mLongPressTimeout;
 
     private KeyguardManager mKeyguardManager;
     private IAudioService mAudioService;
@@ -120,6 +122,7 @@
         mPriorityStack = new MediaSessionStack();
         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
+        mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
     }
 
     @Override
@@ -536,6 +539,16 @@
         return "";
     }
 
+    private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
+        // Only consider full user.
+        UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
+        try {
+            user.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
+        }
+    }
+
     /**
      * Information about a particular user. The contents of this object is
      * guarded by mLock.
@@ -944,7 +957,8 @@
             try {
                 synchronized (mLock) {
                     // Only consider full user.
-                    UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
+                    int userId = mCurrentUserIdList.get(0);
+                    UserRecord user = mUserRecords.get(userId);
 
                     if (mPriorityStack.isGlobalPriorityActive()
                             || user.mOnVolumeKeyLongPressListener == null) {
@@ -954,11 +968,17 @@
                         //       at the same time.
                         if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
                             if (keyEvent.getRepeatCount() == 0) {
-                                user.mInitialDownVolumeKeyEvent = keyEvent;
+                                // Keeps the copy of the KeyEvent because it can be reused.
+                                user.mInitialDownVolumeKeyEvent = KeyEvent.obtain(keyEvent);
                                 user.mInitialDownVolumeStream = stream;
                                 user.mInitialDownMusicOnly = musicOnly;
+                                mHandler.sendMessageDelayed(
+                                        mHandler.obtainMessage(
+                                                MessageHandler.MSG_VOLUME_INITIAL_DOWN, userId, 0),
+                                        mLongPressTimeout);
                             }
                             if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
+                                mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
                                 if (user.mInitialDownVolumeKeyEvent != null) {
                                     dispatchVolumeKeyLongPressLocked(
                                             user.mInitialDownVolumeKeyEvent);
@@ -968,6 +988,7 @@
                                 dispatchVolumeKeyLongPressLocked(keyEvent);
                             }
                         } else { // if up
+                            mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
                             if (user.mInitialDownVolumeKeyEvent != null
                                     && user.mInitialDownVolumeKeyEvent.getDownTime()
                                             == keyEvent.getDownTime()) {
@@ -988,16 +1009,6 @@
             }
         }
 
-        private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
-            // Only consider full user.
-            UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
-            try {
-                user.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
-            }
-        }
-
         private void dispatchVolumeKeyEventLocked(
                 KeyEvent keyEvent, int stream, boolean musicOnly) {
             boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
@@ -1480,6 +1491,7 @@
 
     final class MessageHandler extends Handler {
         private static final int MSG_SESSIONS_CHANGED = 1;
+        private static final int MSG_VOLUME_INITIAL_DOWN = 2;
 
         @Override
         public void handleMessage(Message msg) {
@@ -1487,6 +1499,16 @@
                 case MSG_SESSIONS_CHANGED:
                     pushSessionsChanged(msg.arg1);
                     break;
+                case MSG_VOLUME_INITIAL_DOWN:
+                    synchronized (mLock) {
+                        UserRecord user = mUserRecords.get((int) msg.arg1);
+                        if (user != null && user.mInitialDownVolumeKeyEvent != null) {
+                            dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
+                            // Mark that the key is already handled.
+                            user.mInitialDownVolumeKeyEvent = null;
+                        }
+                    }
+                    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/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 23925ad..55a5f72 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -42,6 +42,7 @@
 import android.util.Xml;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.XmlUtils;
 import libcore.io.IoUtils;
@@ -115,8 +116,14 @@
     }
 
     public byte[] getInstantAppCookieLPw(@NonNull String packageName,
-                                         @UserIdInt int userId) {
-        byte[] pendingCookie = mCookiePersistence.getPendingPersistCookie(userId, packageName);
+            @UserIdInt int userId) {
+        // Only installed packages can get their own cookie
+        PackageParser.Package pkg = mService.mPackages.get(packageName);
+        if (pkg == null) {
+            return null;
+        }
+
+        byte[] pendingCookie = mCookiePersistence.getPendingPersistCookieLPr(pkg, userId);
         if (pendingCookie != null) {
             return pendingCookie;
         }
@@ -132,7 +139,7 @@
     }
 
     public boolean setInstantAppCookieLPw(@NonNull String packageName,
-                                          @Nullable byte[] cookie, @UserIdInt int userId) {
+            @Nullable byte[] cookie, @UserIdInt int userId) {
         if (cookie != null && cookie.length > 0) {
             final int maxCookieSize = mService.mContext.getPackageManager()
                     .getInstantAppCookieMaxSize();
@@ -143,25 +150,25 @@
             }
         }
 
-        mCookiePersistence.schedulePersist(userId, packageName, cookie);
+        // Only an installed package can set its own cookie
+        PackageParser.Package pkg = mService.mPackages.get(packageName);
+        if (pkg == null) {
+            return false;
+        }
+
+        mCookiePersistence.schedulePersistLPw(userId, pkg, cookie);
         return true;
     }
 
     private void persistInstantApplicationCookie(@Nullable byte[] cookie,
-            @NonNull String packageName, @UserIdInt int userId) {
+            @NonNull String packageName, @NonNull File cookieFile, @UserIdInt int userId) {
         synchronized (mService.mPackages) {
-            PackageParser.Package pkg = mService.mPackages.get(packageName);
-            if (pkg == null) {
-                return;
-            }
-
             File appDir = getInstantApplicationDir(packageName, userId);
             if (!appDir.exists() && !appDir.mkdirs()) {
                 Slog.e(LOG_TAG, "Cannot create instant app cookie directory");
                 return;
             }
 
-            File cookieFile = computeInstantCookieFile(pkg, userId);
             if (cookieFile.exists() && !cookieFile.delete()) {
                 Slog.e(LOG_TAG, "Cannot delete instant app cookie file");
             }
@@ -170,12 +177,11 @@
             if (cookie == null || cookie.length <= 0) {
                 return;
             }
-
-            try (FileOutputStream fos = new FileOutputStream(cookieFile)) {
-                fos.write(cookie, 0, cookie.length);
-            } catch (IOException e) {
-                Slog.e(LOG_TAG, "Error writing instant app cookie file: " + cookieFile, e);
-            }
+        }
+        try (FileOutputStream fos = new FileOutputStream(cookieFile)) {
+            fos.write(cookie, 0, cookie.length);
+        } catch (IOException e) {
+            Slog.e(LOG_TAG, "Error writing instant app cookie file: " + cookieFile, e);
         }
     }
 
@@ -240,6 +246,8 @@
             if (!currentCookieFile.equals(expectedCookeFile)) {
                 Slog.i(LOG_TAG, "Signature for package " + pkg.packageName
                         + " changed - dropping cookie");
+                // Make sure a pending write for the old signed app is cancelled
+                mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
                 currentCookieFile.delete();
             }
         }
@@ -890,71 +898,90 @@
         // In case you wonder why we stash the cookies aside, we use
         // the user id for the message id and the package for the payload.
         // Handler allows removing messages by id and tag where the
-        // tag is is compared using ==. So to allow cancelling the
+        // tag is compared using ==. So to allow cancelling the
         // pending persistence for an app under a given user we use
-        // the fact that package names are interned in the system
-        // process so the == comparison would match and we end up
-        // with a way to cancel persisting the cookie for a user
-        // and package.
-        private final SparseArray<ArrayMap<String, byte[]>> mPendingPersistCookies =
-                new SparseArray<>();
+        // the fact that package are cached by the system so the ==
+        // comparison would match and we end up with a way to cancel
+        // persisting the cookie for a user and package.
+        private final SparseArray<ArrayMap<PackageParser.Package, SomeArgs>> mPendingPersistCookies
+                = new SparseArray<>();
 
         public CookiePersistence(Looper looper) {
             super(looper);
         }
 
-        public void schedulePersist(@UserIdInt int userId,
-                @NonNull String packageName, @NonNull byte[] cookie) {
-            cancelPendingPersist(userId, packageName);
-            addPendingPersistCookie(userId, packageName, cookie);
-            sendMessageDelayed(obtainMessage(userId, packageName),
+        public void schedulePersistLPw(@UserIdInt int userId, @NonNull PackageParser.Package pkg,
+                @NonNull byte[] cookie) {
+            File cookieFile = computeInstantCookieFile(pkg, userId);
+            cancelPendingPersistLPw(pkg, userId);
+            addPendingPersistCookieLPw(userId, pkg, cookie, cookieFile);
+            sendMessageDelayed(obtainMessage(userId, pkg),
                     PERSIST_COOKIE_DELAY_MILLIS);
         }
 
-        public @Nullable byte[] getPendingPersistCookie(@UserIdInt int userId,
-                @NonNull String packageName) {
-            ArrayMap<String, byte[]> pendingWorkForUser = mPendingPersistCookies.get(userId);
+        public @Nullable byte[] getPendingPersistCookieLPr(@NonNull PackageParser.Package pkg,
+                @UserIdInt int userId) {
+            ArrayMap<PackageParser.Package, SomeArgs> pendingWorkForUser =
+                    mPendingPersistCookies.get(userId);
             if (pendingWorkForUser != null) {
-                return pendingWorkForUser.remove(packageName);
+                SomeArgs state = pendingWorkForUser.get(pkg);
+                if (state != null) {
+                    return (byte[]) state.arg1;
+                }
             }
             return null;
         }
 
-        private void cancelPendingPersist(@UserIdInt int userId,
-                @NonNull String packageName) {
-            removePendingPersistCookie(userId, packageName);
-            removeMessages(userId, packageName);
+        public void cancelPendingPersistLPw(@NonNull PackageParser.Package pkg,
+                @UserIdInt int userId) {
+            removeMessages(userId, pkg);
+            SomeArgs state = removePendingPersistCookieLPr(pkg, userId);
+            if (state != null) {
+                state.recycle();
+            }
         }
 
-        private void addPendingPersistCookie(@UserIdInt int userId,
-                @NonNull String packageName, @NonNull byte[] cookie) {
-            ArrayMap<String, byte[]> pendingWorkForUser = mPendingPersistCookies.get(userId);
+        private void addPendingPersistCookieLPw(@UserIdInt int userId,
+                @NonNull PackageParser.Package pkg, @NonNull byte[] cookie,
+                @NonNull File cookieFile) {
+            ArrayMap<PackageParser.Package, SomeArgs> pendingWorkForUser =
+                    mPendingPersistCookies.get(userId);
             if (pendingWorkForUser == null) {
                 pendingWorkForUser = new ArrayMap<>();
                 mPendingPersistCookies.put(userId, pendingWorkForUser);
             }
-            pendingWorkForUser.put(packageName, cookie);
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = cookie;
+            args.arg2 = cookieFile;
+            pendingWorkForUser.put(pkg, args);
         }
 
-        private byte[] removePendingPersistCookie(@UserIdInt int userId,
-                @NonNull String packageName) {
-            ArrayMap<String, byte[]> pendingWorkForUser = mPendingPersistCookies.get(userId);
-            byte[] cookie = null;
+        private SomeArgs removePendingPersistCookieLPr(@NonNull PackageParser.Package pkg,
+                @UserIdInt int userId) {
+            ArrayMap<PackageParser.Package, SomeArgs> pendingWorkForUser =
+                    mPendingPersistCookies.get(userId);
+            SomeArgs state = null;
             if (pendingWorkForUser != null) {
-                cookie = pendingWorkForUser.remove(packageName);
+                state = pendingWorkForUser.remove(pkg);
                 if (pendingWorkForUser.isEmpty()) {
                     mPendingPersistCookies.remove(userId);
                 }
             }
-            return cookie;
+            return state;
         }
 
         @Override
         public void handleMessage(Message message) {
             int userId = message.what;
-            String packageName = (String) message.obj;
-            byte[] cookie = removePendingPersistCookie(userId, packageName);
-            persistInstantApplicationCookie(cookie, packageName, userId);
+            PackageParser.Package pkg = (PackageParser.Package) message.obj;
+            SomeArgs state = removePendingPersistCookieLPr(pkg, userId);
+            if (state == null) {
+                return;
+            }
+            byte[] cookie = (byte[]) state.arg1;
+            File cookieFile = (File) state.arg2;
+            state.recycle();
+            persistInstantApplicationCookie(cookie, pkg.packageName, cookieFile, userId);
         }
     }
 }
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/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index ffbb428..b76a249 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -674,9 +674,6 @@
                     mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
                     mPolicy);
 
-            final ContentResolver resolver = mContext.getContentResolver();
-            mConstants.start(resolver);
-
             mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
                     createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
                     mHandler);
@@ -689,57 +686,6 @@
             mDisplayManagerInternal.initPowerManagement(
                     mDisplayPowerCallbacks, mHandler, sensorManager);
 
-            // Register for settings changes.
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.SCREENSAVER_ENABLED),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.System.getUriFor(
-                    Settings.System.SCREEN_OFF_TIMEOUT),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.SLEEP_TIMEOUT),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.STAY_ON_WHILE_PLUGGED_IN),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.System.getUriFor(
-                    Settings.System.SCREEN_BRIGHTNESS),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.System.getUriFor(
-                    Settings.System.SCREEN_BRIGHTNESS_FOR_VR),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.System.getUriFor(
-                    Settings.System.SCREEN_BRIGHTNESS_MODE),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.System.getUriFor(
-                    Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.LOW_POWER_MODE),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.THEATER_MODE_ON),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.DOUBLE_TAP_TO_WAKE),
-                    false, mSettingsObserver, UserHandle.USER_ALL);
-            IVrManager vrManager = (IVrManager) getBinderService(Context.VR_SERVICE);
-            if (vrManager != null) {
-                try {
-                    vrManager.registerListener(mVrStateCallbacks);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Failed to register VR mode state listener: " + e);
-                }
-            }
             // Go.
             readConfigurationLocked();
             updateSettingsLocked();
@@ -747,6 +693,61 @@
             updatePowerStateLocked();
         }
 
+        final ContentResolver resolver = mContext.getContentResolver();
+        mConstants.start(resolver);
+
+        // Register for settings changes.
+        resolver.registerContentObserver(Settings.Secure.getUriFor(
+                Settings.Secure.SCREENSAVER_ENABLED),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Secure.getUriFor(
+                Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Secure.getUriFor(
+                Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.System.getUriFor(
+                Settings.System.SCREEN_OFF_TIMEOUT),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Secure.getUriFor(
+                Settings.Secure.SLEEP_TIMEOUT),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.System.getUriFor(
+                Settings.System.SCREEN_BRIGHTNESS),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.System.getUriFor(
+                Settings.System.SCREEN_BRIGHTNESS_FOR_VR),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.System.getUriFor(
+                Settings.System.SCREEN_BRIGHTNESS_MODE),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.System.getUriFor(
+                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.LOW_POWER_MODE),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.THEATER_MODE_ON),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Secure.getUriFor(
+                Settings.Secure.DOUBLE_TAP_TO_WAKE),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        IVrManager vrManager = (IVrManager) getBinderService(Context.VR_SERVICE);
+        if (vrManager != null) {
+            try {
+                vrManager.registerListener(mVrStateCallbacks);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+            }
+        }
+
         // Register for broadcasts from other components of the system.
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ff732ed..14b1741 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -194,7 +194,6 @@
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.text.DateFormat;
-import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -2912,41 +2911,11 @@
         }
     }
 
-    private void ensureUnknownSourcesRestrictionForProfileOwners() {
-        synchronized (this) {
-            for (int userId : mOwners.getProfileOwnerKeys()) {
-                if (!mUserManager.isManagedProfile(userId) ||
-                        Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                        Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId) == 0) {
-                    continue;
-                }
-                setUserRestrictionOnBehalfOfProfileOwnerLocked(
-                        UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId);
-                Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                        Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId);
-            }
-        }
-    }
-
-    private void setUserRestrictionOnBehalfOfProfileOwnerLocked(String userRestrictionKey,
-            int userId) {
-        if (UserRestrictionsUtils.isValidRestriction(userRestrictionKey) &&
-                UserRestrictionsUtils.canProfileOwnerChange(userRestrictionKey, userId)) {
-            ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
-            if (profileOwner == null) {
-                return;
-            }
-            Bundle restrictions = profileOwner.ensureUserRestrictions();
-            restrictions.putBoolean(userRestrictionKey, true);
-            saveUserRestrictionsLocked(userId);
-        }
-    }
-
     private void onLockSettingsReady() {
         getUserData(UserHandle.USER_SYSTEM);
         loadOwners();
         cleanUpOldUsers();
-        ensureUnknownSourcesRestrictionForProfileOwners();
+
         onStartUser(UserHandle.USER_SYSTEM);
 
         // Register an observer for watching for user setup complete and settings changes.
@@ -6669,12 +6638,6 @@
             mOwners.writeProfileOwner(userHandle);
             Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
 
-            if (mUserManager.isManagedProfile(userHandle)) {
-                setUserRestrictionOnBehalfOfProfileOwnerLocked(
-                        UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userHandle);
-                Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                        Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userHandle);
-            }
             return true;
         }
     }
@@ -8761,27 +8724,7 @@
                 throw new SecurityException(String.format(
                         "Permission denial: Profile owners cannot update %1$s", setting));
             }
-            if (setting.equals(Settings.Secure.INSTALL_NON_MARKET_APPS)) {
-                if (getTargetSdk(who.getPackageName(), callingUserId) >= Build.VERSION_CODES.O) {
-                    throw new UnsupportedOperationException(Settings.Secure.INSTALL_NON_MARKET_APPS
-                            + " is deprecated. Please use the user restriction "
-                            + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES + " instead.");
-                }
-                if (!mUserManager.isManagedProfile(callingUserId)) {
-                    Slog.e(LOG_TAG, "Ignoring setSecureSetting request for "
-                            + setting + ". User restriction "
-                            + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES
-                            + " should be used instead.");
-                } else {
-                    try {
-                        setUserRestriction(who, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
-                                (Integer.parseInt(value) == 0) ? true : false);
-                    } catch (NumberFormatException exc) {
-                        Slog.e(LOG_TAG, "Invalid value: " + value + " for setting " + setting);
-                    }
-                }
-                return;
-            }
+
             long id = mInjector.binderClearCallingIdentity();
             try {
                 if (Settings.Secure.DEFAULT_INPUT_METHOD.equals(setting)) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 5591029..384f49f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -140,6 +140,36 @@
         assertAllFieldsExist(deserialized);
     }
 
+    @Test
+    public void test_stringInterning() throws Exception {
+        PackageParser.Package pkg = new PackageParser.Package("foo");
+        setKnownFields(pkg);
+
+        Parcel p = Parcel.obtain();
+        pkg.writeToParcel(p, 0 /* flags */);
+
+        p.setDataPosition(0);
+        PackageParser.Package deserialized = new PackageParser.Package(p);
+
+        p.setDataPosition(0);
+        PackageParser.Package deserialized2 = new PackageParser.Package(p);
+
+        assertSame(deserialized.packageName, deserialized2.packageName);
+        assertSame(deserialized.applicationInfo.permission,
+                deserialized2.applicationInfo.permission);
+        assertSame(deserialized.requestedPermissions.get(0),
+                deserialized2.requestedPermissions.get(0));
+        assertSame(deserialized.protectedBroadcasts.get(0),
+                deserialized2.protectedBroadcasts.get(0));
+        assertSame(deserialized.usesLibraries.get(0),
+                deserialized2.usesLibraries.get(0));
+        assertSame(deserialized.usesOptionalLibraries.get(0),
+                deserialized2.usesOptionalLibraries.get(0));
+        assertSame(deserialized.mVersionName, deserialized2.mVersionName);
+        assertSame(deserialized.mSharedUserId, deserialized2.mSharedUserId);
+    }
+
+
     /**
      * A trivial subclass of package parser that only caches the package name, and throws away
      * all other information.
@@ -407,11 +437,11 @@
         pkg.mTrustedOverlay = true;
         pkg.use32bitAbi = true;
         pkg.packageName = "foo";
-        pkg.splitNames = new String[] { "foo" };
-        pkg.volumeUuid = "foo";
-        pkg.codePath = "foo";
-        pkg.baseCodePath = "foo";
-        pkg.splitCodePaths = new String[] { "foo" };
+        pkg.splitNames = new String[] { "foo2" };
+        pkg.volumeUuid = "foo3";
+        pkg.codePath = "foo4";
+        pkg.baseCodePath = "foo5";
+        pkg.splitCodePaths = new String[] { "foo6" };
         pkg.splitRevisionCodes = new int[] { 100 };
         pkg.splitFlags = new int[] { 100 };
         pkg.splitPrivateFlags = new int[] { 100 };
@@ -428,48 +458,55 @@
         pkg.providers.add(new PackageParser.Provider(dummy, new ProviderInfo()));
         pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo()));
         pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo()));
-        pkg.requestedPermissions.add("foo");
+        pkg.requestedPermissions.add("foo7");
 
         pkg.protectedBroadcasts = new ArrayList<>();
-        pkg.protectedBroadcasts.add("foo");
+        pkg.protectedBroadcasts.add("foo8");
 
-        pkg.parentPackage = new PackageParser.Package("foo");
+        pkg.parentPackage = new PackageParser.Package("foo9");
 
         pkg.childPackages = new ArrayList<>();
         pkg.childPackages.add(new PackageParser.Package("bar"));
 
+        pkg.staticSharedLibName = "foo23";
+        pkg.staticSharedLibVersion = 100;
+        pkg.usesStaticLibraries = new ArrayList<>();
+        pkg.usesStaticLibraries.add("foo23");
+        pkg.usesStaticLibrariesCertDigests = new String[] { "digest" };
+        pkg.usesStaticLibrariesVersions = new int[] { 100 };
+
         pkg.libraryNames = new ArrayList<>();
-        pkg.libraryNames.add("foo");
+        pkg.libraryNames.add("foo10");
 
         pkg.usesLibraries = new ArrayList<>();
-        pkg.usesLibraries.add("foo");
+        pkg.usesLibraries.add("foo11");
 
         pkg.usesOptionalLibraries = new ArrayList<>();
-        pkg.usesOptionalLibraries.add("foo");
+        pkg.usesOptionalLibraries.add("foo12");
 
-        pkg.usesLibraryFiles = new String[] { "foo "};
+        pkg.usesLibraryFiles = new String[] { "foo13"};
 
         pkg.mOriginalPackages = new ArrayList<>();
-        pkg.mOriginalPackages.add("foo");
+        pkg.mOriginalPackages.add("foo14");
 
-        pkg.mRealPackage = "foo";
+        pkg.mRealPackage = "foo15";
 
         pkg.mAdoptPermissions = new ArrayList<>();
-        pkg.mAdoptPermissions.add("foo");
+        pkg.mAdoptPermissions.add("foo16");
 
         pkg.mAppMetaData = new Bundle();
-        pkg.mVersionName = "foo";
-        pkg.mSharedUserId = "foo";
+        pkg.mVersionName = "foo17";
+        pkg.mSharedUserId = "foo18";
         pkg.mSignatures = new Signature[] { new Signature(new byte[16]) };
         pkg.mCertificates = new Certificate[][] { new Certificate[] { null }};
         pkg.mExtras = new Bundle();
-        pkg.mRestrictedAccountType = "foo";
-        pkg.mRequiredAccountType = "foo";
-        pkg.mOverlayTarget = "foo";
+        pkg.mRestrictedAccountType = "foo19";
+        pkg.mRequiredAccountType = "foo20";
+        pkg.mOverlayTarget = "foo21";
         pkg.mSigningKeys = new ArraySet<>();
         pkg.mUpgradeKeySets = new ArraySet<>();
         pkg.mKeySetMapping = new ArrayMap<>();
-        pkg.cpuAbiOverride = "foo";
+        pkg.cpuAbiOverride = "foo22";
         pkg.restrictUpdateHash = new byte[16];
 
         pkg.preferredActivityFilters = new ArrayList<>();
@@ -504,7 +541,7 @@
                 // Sanity check for list fields: Assume they're non-null and contain precisely
                 // one element.
                 List<?> list = (List<?>) f.get(pkg);
-                assertNotNull(list);
+                assertNotNull("List was null: " + f, list);
                 assertEquals(1, list.size());
             } else if (fieldType.getComponentType() != null) {
                 // Sanity check for array fields: Assume they're non-null and contain precisely
@@ -514,15 +551,16 @@
             } else if (fieldType == String.class) {
                 // String fields: Check that they're set to "foo".
                 String value = (String) f.get(pkg);
-                assertEquals("foo", value);
+
+                assertTrue("Bad value for field: " + f, value != null && value.startsWith("foo"));
             } else if (fieldType == int.class) {
                 // int fields: Check that they're set to 100.
                 int value = (int) f.get(pkg);
-                assertEquals(100, value);
+                assertEquals("Bad value for field: " + f, 100, value);
             } else {
                 // All other fields: Check that they're set.
                 Object o = f.get(pkg);
-                assertNotNull("Field was null: " + f.getName(), o);
+                assertNotNull("Field was null: " + f, o);
             }
         }
     }
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/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/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index cf5badc..881ac87 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -721,11 +721,6 @@
     XMLNode::attribute_entry* existingEntry = node->editAttribute(ns, attr);
     if (existingEntry != NULL) {
         if (replaceExisting) {
-            if (kIsDebug) {
-                printf("Info: AndroidManifest.xml already defines %s (in %s);"
-                        " overwriting existing value from manifest.\n",
-                        String8(attr).string(), String8(ns).string());
-            }
             existingEntry->string = String16(value);
             return true;
         }
@@ -737,10 +732,6 @@
             return false;
         }
 
-        fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
-                        " using existing value in manifest.\n",
-                String8(attr).string(), String8(ns).string());
-
         // don't stop the build.
         return true;
     }
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 9bc8e18..2c7e936 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -638,6 +638,9 @@
                 return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue);
             }
         }
+        else if (value.startsWith("@aapt:_aapt")) {
+            return mContext.getLayoutlibCallback().getResourceId(ResourceType.AAPT, value);
+        }
 
         // not a direct id valid reference. First check if it's an enum (this is a corner case
         // for attributes that have a reference|enum type), then fallback to resolve
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
index 6e3a8e8..c20ee12 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
@@ -131,9 +131,16 @@
         if (resourceInfo != null) {
             String attributeName = resourceInfo.getSecond();
             RenderResources renderResources = resources.mContext.getRenderResources();
-            return Pair.of(attributeName, platformResFlag_out[0] ?
+            ResourceValue value = platformResFlag_out[0] ?
                     renderResources.getFrameworkResource(resourceInfo.getFirst(), attributeName) :
-                    renderResources.getProjectResource(resourceInfo.getFirst(), attributeName));
+                    renderResources.getProjectResource(resourceInfo.getFirst(), attributeName);
+
+            if (value == null) {
+                // Unable to resolve the attribute, just leave the unresolved value
+                value = new ResourceValue(resourceInfo.getFirst(), attributeName, attributeName,
+                        platformResFlag_out[0]);
+            }
+            return Pair.of(attributeName, value);
         }
 
         return null;
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index fb24c01..ff5a5e9 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -314,7 +314,7 @@
 
     @LayoutlibDelegate
     /*package*/ static boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr, String path,
-            int cookie, boolean isAsset) {
+            int cookie, boolean isAsset, int weight, boolean isItalic) {
         FontFamily_Delegate ffd = sManager.getDelegate(builderPtr);
         if (ffd == null) {
             return false;
@@ -355,8 +355,12 @@
                 Font font = Font.createFont(Font.TRUETYPE_FONT, fontStream);
                 fontInfo = new FontInfo();
                 fontInfo.mFont = font;
-                fontInfo.mWeight = font.isBold() ? BOLD_FONT_WEIGHT : DEFAULT_FONT_WEIGHT;
-                fontInfo.mIsItalic = font.isItalic();
+                if (weight == 0) {
+                    fontInfo.mWeight = font.isBold() ? BOLD_FONT_WEIGHT : DEFAULT_FONT_WEIGHT;
+                } else {
+                    fontInfo.mWeight = weight;
+                }
+                fontInfo.mIsItalic = weight == 0 ? font.isItalic() : isItalic;
                 ffd.addFont(fontInfo);
                 return true;
             } catch (IOException e) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
index 91668af..e10f20d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -100,7 +100,8 @@
         mView.setText(text);
     }
 
-    private void setGravity(int gravity) {
+    @SuppressWarnings("WeakerAccess") // This method is used from Studio
+    public void setGravity(int gravity) {
         mView.setGravity(gravity);
     }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 2c9fe29..3d5d5c6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -65,6 +65,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
 import android.net.Uri;
@@ -381,6 +382,18 @@
             return true;
         }
 
+        String stringValue = value.getValue();
+        if (!stringValue.isEmpty()) {
+            if (stringValue.charAt(0) == '#') {
+                outValue.type = TypedValue.TYPE_INT_COLOR_ARGB8;
+                outValue.data = Color.parseColor(value.getValue());
+            }
+            else if (stringValue.charAt(0) == '@') {
+                outValue.type = TypedValue.TYPE_REFERENCE;
+            }
+
+        }
+
         int a;
         // if this is a framework value.
         if (value.isFramework()) {
@@ -399,7 +412,7 @@
         }
 
         // If the value is not a valid reference, fallback to pass the value as a string.
-        outValue.string = value.getValue();
+        outValue.string = stringValue;
         return true;
     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index b3a2d3e..f1e7b51 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -286,18 +286,15 @@
 
         Density density = Density.MEDIUM;
         if (value instanceof DensityBasedResourceValue) {
-            density =
-                ((DensityBasedResourceValue)value).getResourceDensity();
+            density = ((DensityBasedResourceValue) value).getResourceDensity();
         }
 
-
         if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
             File file = new File(stringValue);
             if (file.isFile()) {
                 try {
-                    return getNinePatchDrawable(
-                            new FileInputStream(file), density, value.isFramework(),
-                            stringValue, context);
+                    return getNinePatchDrawable(new FileInputStream(file), density,
+                            value.isFramework(), stringValue, context);
                 } catch (IOException e) {
                     // failed to read the file, we'll return null below.
                     Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
@@ -306,30 +303,28 @@
             }
 
             return null;
-        } else if (lowerCaseValue.endsWith(".xml")) {
+        } else if (lowerCaseValue.endsWith(".xml") || stringValue.startsWith("@aapt:_aapt/")) {
             // create a block parser for the file
-            File f = new File(stringValue);
-            if (f.isFile()) {
-                try {
-                    // let the framework inflate the Drawable from the XML file.
-                    XmlPullParser parser = ParserFactory.create(f);
-
-                    BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
-                            parser, context, value.isFramework());
-                    try {
-                        return Drawable.createFromXml(context.getResources(), blockParser, theme);
-                    } finally {
-                        blockParser.ensurePopped();
+            try {
+                XmlPullParser parser = context.getLayoutlibCallback().getParser(value);
+                if (parser == null) {
+                    File drawableFile = new File(stringValue);
+                    if (drawableFile.isFile()) {
+                        parser = ParserFactory.create(drawableFile);
                     }
-                } catch (Exception e) {
-                    // this is an error and not warning since the file existence is checked before
-                    // attempting to parse it.
-                    Bridge.getLog().error(null, "Failed to parse file " + stringValue,
-                            e, null /*data*/);
                 }
-            } else {
-                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        String.format("File %s does not exist (or is not a file)", stringValue),
+
+                BridgeXmlBlockParser blockParser =
+                        new BridgeXmlBlockParser(parser, context, value.isFramework());
+                try {
+                    return Drawable.createFromXml(context.getResources(), blockParser, theme);
+                } finally {
+                    blockParser.ensurePopped();
+                }
+            } catch (Exception e) {
+                // this is an error and not warning since the file existence is checked before
+                // attempting to parse it.
+                Bridge.getLog().error(null, "Failed to parse file " + stringValue, e,
                         null /*data*/);
             }
 
@@ -342,8 +337,8 @@
                             value.isFramework() ? null : context.getProjectKey());
 
                     if (bitmap == null) {
-                        bitmap = Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/,
-                                density);
+                        bitmap =
+                                Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/, density);
                         Bridge.setCachedBitmap(stringValue, bitmap,
                                 value.isFramework() ? null : context.getProjectKey());
                     }
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
index 913519c..9e60f0f 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -36,6 +36,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.util.DisplayMetrics;
+import android.util.TypedValue;
 
 import java.lang.reflect.Field;
 import java.util.concurrent.TimeUnit;
@@ -43,6 +44,7 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -334,6 +336,7 @@
         AssetManager assetManager = AssetManager.getSystem();
         DisplayMetrics metrics = new DisplayMetrics();
         Configuration configuration = RenderAction.getConfiguration(params);
+        //noinspection deprecation
         Resources resources = new Resources(assetManager, metrics, configuration);
         resources.mLayoutlibCallback = params.getLayoutlibCallback();
         resources.mContext =
@@ -370,6 +373,7 @@
         AssetManager assetManager = AssetManager.getSystem();
         DisplayMetrics metrics = new DisplayMetrics();
         Configuration configuration = RenderAction.getConfiguration(params);
+        //noinspection deprecation
         Resources resources = new Resources(assetManager, metrics, configuration);
         resources.mLayoutlibCallback = params.getLayoutlibCallback();
         resources.mContext =
@@ -390,4 +394,34 @@
         // TODO: styles seem to be broken in TextView
         renderAndVerify("fonts_test.xml", "font_test.png");
     }
+
+    @Test
+    public void testColorTypedValue() throws Exception {
+        // Setup
+        // Create the layout pull parser for our resources (empty.xml can not be part of the test
+        // app as it won't compile).
+        LayoutPullParser parser = new LayoutPullParser("/empty.xml");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4,
+                layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
+        AssetManager assetManager = AssetManager.getSystem();
+        DisplayMetrics metrics = new DisplayMetrics();
+        Configuration configuration = RenderAction.getConfiguration(params);
+        //noinspection deprecation
+        Resources resources = new Resources(assetManager, metrics, configuration);
+        resources.mLayoutlibCallback = params.getLayoutlibCallback();
+        resources.mContext =
+                new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+                        params.getAssets(), params.getLayoutlibCallback(), configuration,
+                        params.getTargetSdkVersion(), params.isRtlSupported());
+
+        TypedValue outValue = new TypedValue();
+        resources.mContext.resolveThemeAttribute(android.R.attr.colorPrimary, outValue, true);
+        assertEquals(TypedValue.TYPE_INT_COLOR_ARGB8, outValue.type);
+        assertNotEquals(0, outValue.data);
+        assertTrue(sRenderMessages.isEmpty());
+    }
 }