Merge "Do not show volume expander when there is no touch feature"
diff --git a/Android.mk b/Android.mk
index c1c74ea..b95ab2e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -345,6 +345,7 @@
 	media/java/android/media/IMediaRouterService.aidl \
 	media/java/android/media/IMediaScannerListener.aidl \
 	media/java/android/media/IMediaScannerService.aidl \
+	media/java/android/media/IRecordingConfigDispatcher.aidl \
 	media/java/android/media/IRemoteDisplayCallback.aidl \
 	media/java/android/media/IRemoteDisplayProvider.aidl \
 	media/java/android/media/IRemoteVolumeController.aidl \
diff --git a/api/current.txt b/api/current.txt
index b0eb006..629d832 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5793,6 +5793,7 @@
     method public boolean getCameraDisabled(android.content.ComponentName);
     method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
+    method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
     method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
     method public java.lang.String getDeviceOwnerLockScreenInfo();
@@ -5857,6 +5858,7 @@
     method public void setCameraDisabled(android.content.ComponentName, boolean);
     method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
     method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+    method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
     method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
@@ -12784,6 +12786,7 @@
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
     field public static final int PADDING_MODE_NEST = 0; // 0x0
     field public static final int PADDING_MODE_STACK = 1; // 0x1
+    field public static final int UNDEFINED_INSET = -2147483648; // 0x80000000
   }
 
   public class LevelListDrawable extends android.graphics.drawable.DrawableContainer {
@@ -20368,8 +20371,6 @@
     field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
     field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
     field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
-    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
-    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
     field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
@@ -22579,7 +22580,16 @@
 
   public class MtpEvent {
     ctor public MtpEvent();
+    method public int getDevicePropCode();
     method public int getEventCode();
+    method public int getObjectFormatCode();
+    method public int getObjectHandle();
+    method public int getObjectPropCode();
+    method public int getParameter1();
+    method public int getParameter2();
+    method public int getParameter3();
+    method public int getStorageId();
+    method public int getTransactionId();
   }
 
   public final class MtpObjectInfo {
@@ -49542,6 +49552,8 @@
   public class InternalError extends java.lang.VirtualMachineError {
     ctor public InternalError();
     ctor public InternalError(java.lang.String);
+    ctor public InternalError(java.lang.String, java.lang.Throwable);
+    ctor public InternalError(java.lang.Throwable);
   }
 
   public class InterruptedException extends java.lang.Exception {
@@ -50314,6 +50326,8 @@
   public abstract class VirtualMachineError extends java.lang.Error {
     ctor public VirtualMachineError();
     ctor public VirtualMachineError(java.lang.String);
+    ctor public VirtualMachineError(java.lang.String, java.lang.Throwable);
+    ctor public VirtualMachineError(java.lang.Throwable);
   }
 
   public final class Void {
@@ -54056,6 +54070,7 @@
     method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
   }
 
   public class CertPathBuilderException extends java.security.GeneralSecurityException {
@@ -54073,6 +54088,13 @@
   public abstract class CertPathBuilderSpi {
     ctor public CertPathBuilderSpi();
     method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException;
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
+  }
+
+  public abstract interface CertPathChecker {
+    method public abstract void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
+    method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
+    method public abstract boolean isForwardCheckingSupported();
   }
 
   public abstract interface CertPathParameters implements java.lang.Cloneable {
@@ -54087,6 +54109,7 @@
     method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
     method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
   }
 
@@ -54123,6 +54146,7 @@
 
   public abstract class CertPathValidatorSpi {
     ctor public CertPathValidatorSpi();
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
     method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
   }
 
@@ -54281,9 +54305,10 @@
     method public java.security.cert.CertPath getCertPath();
   }
 
-  public abstract class PKIXCertPathChecker implements java.lang.Cloneable {
+  public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable {
     ctor protected PKIXCertPathChecker();
     method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException;
+    method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
     method public java.lang.Object clone();
     method public abstract java.util.Set<java.lang.String> getSupportedExtensions();
     method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
@@ -54343,6 +54368,30 @@
     enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT;
   }
 
+  public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker {
+    ctor protected PKIXRevocationChecker();
+    method public java.util.List<java.security.cert.Extension> getOcspExtensions();
+    method public java.net.URI getOcspResponder();
+    method public java.security.cert.X509Certificate getOcspResponderCert();
+    method public java.util.Map<java.security.cert.X509Certificate, byte[]> getOcspResponses();
+    method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions();
+    method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions();
+    method public void setOcspExtensions(java.util.List<java.security.cert.Extension>);
+    method public void setOcspResponder(java.net.URI);
+    method public void setOcspResponderCert(java.security.cert.X509Certificate);
+    method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate, byte[]>);
+    method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>);
+  }
+
+  public static final class PKIXRevocationChecker.Option extends java.lang.Enum {
+    method public static java.security.cert.PKIXRevocationChecker.Option valueOf(java.lang.String);
+    method public static final java.security.cert.PKIXRevocationChecker.Option[] values();
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL;
+  }
+
   public abstract interface PolicyNode {
     method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren();
     method public abstract int getDepth();
@@ -54502,6 +54551,7 @@
     method public javax.security.auth.x500.X500Principal getSubjectX500Principal();
     method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException;
     method public abstract int getVersion();
+    method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
   }
 
   public abstract interface X509Extension {
diff --git a/api/system-current.txt b/api/system-current.txt
index bd7dc97..6a36ce0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5919,6 +5919,7 @@
     method public boolean getCameraDisabled(android.content.ComponentName);
     method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
+    method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
     method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
     method public deprecated java.lang.String getDeviceInitializerApp();
@@ -5992,6 +5993,7 @@
     method public void setCameraDisabled(android.content.ComponentName, boolean);
     method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
     method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+    method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
     method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
@@ -13139,6 +13141,7 @@
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
     field public static final int PADDING_MODE_NEST = 0; // 0x0
     field public static final int PADDING_MODE_STACK = 1; // 0x1
+    field public static final int UNDEFINED_INSET = -2147483648; // 0x80000000
   }
 
   public class LevelListDrawable extends android.graphics.drawable.DrawableContainer {
@@ -21667,8 +21670,6 @@
     field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
     field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
     field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
-    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
-    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
     field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
@@ -24126,7 +24127,16 @@
 
   public class MtpEvent {
     ctor public MtpEvent();
+    method public int getDevicePropCode();
     method public int getEventCode();
+    method public int getObjectFormatCode();
+    method public int getObjectHandle();
+    method public int getObjectPropCode();
+    method public int getParameter1();
+    method public int getParameter2();
+    method public int getParameter3();
+    method public int getStorageId();
+    method public int getTransactionId();
   }
 
   public final class MtpObjectInfo {
@@ -24207,6 +24217,7 @@
     method public android.net.Network[] getAllNetworks();
     method public deprecated boolean getBackgroundDataSetting();
     method public android.net.Network getBoundNetworkForProcess();
+    method public java.lang.String getCaptivePortalServerUrl();
     method public android.net.ProxyInfo getDefaultProxy();
     method public android.net.LinkProperties getLinkProperties(android.net.Network);
     method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network);
@@ -37913,6 +37924,7 @@
     field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
     field public static final java.lang.String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
     field public static final java.lang.String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
+    field public static final java.lang.String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
     field public static final java.lang.String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
     field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
     field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
@@ -52207,6 +52219,8 @@
   public class InternalError extends java.lang.VirtualMachineError {
     ctor public InternalError();
     ctor public InternalError(java.lang.String);
+    ctor public InternalError(java.lang.String, java.lang.Throwable);
+    ctor public InternalError(java.lang.Throwable);
   }
 
   public class InterruptedException extends java.lang.Exception {
@@ -52979,6 +52993,8 @@
   public abstract class VirtualMachineError extends java.lang.Error {
     ctor public VirtualMachineError();
     ctor public VirtualMachineError(java.lang.String);
+    ctor public VirtualMachineError(java.lang.String, java.lang.Throwable);
+    ctor public VirtualMachineError(java.lang.Throwable);
   }
 
   public final class Void {
@@ -56721,6 +56737,7 @@
     method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
   }
 
   public class CertPathBuilderException extends java.security.GeneralSecurityException {
@@ -56738,6 +56755,13 @@
   public abstract class CertPathBuilderSpi {
     ctor public CertPathBuilderSpi();
     method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException;
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
+  }
+
+  public abstract interface CertPathChecker {
+    method public abstract void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
+    method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
+    method public abstract boolean isForwardCheckingSupported();
   }
 
   public abstract interface CertPathParameters implements java.lang.Cloneable {
@@ -56752,6 +56776,7 @@
     method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
     method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
   }
 
@@ -56788,6 +56813,7 @@
 
   public abstract class CertPathValidatorSpi {
     ctor public CertPathValidatorSpi();
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
     method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
   }
 
@@ -56946,9 +56972,10 @@
     method public java.security.cert.CertPath getCertPath();
   }
 
-  public abstract class PKIXCertPathChecker implements java.lang.Cloneable {
+  public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable {
     ctor protected PKIXCertPathChecker();
     method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException;
+    method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
     method public java.lang.Object clone();
     method public abstract java.util.Set<java.lang.String> getSupportedExtensions();
     method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
@@ -57008,6 +57035,30 @@
     enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT;
   }
 
+  public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker {
+    ctor protected PKIXRevocationChecker();
+    method public java.util.List<java.security.cert.Extension> getOcspExtensions();
+    method public java.net.URI getOcspResponder();
+    method public java.security.cert.X509Certificate getOcspResponderCert();
+    method public java.util.Map<java.security.cert.X509Certificate, byte[]> getOcspResponses();
+    method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions();
+    method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions();
+    method public void setOcspExtensions(java.util.List<java.security.cert.Extension>);
+    method public void setOcspResponder(java.net.URI);
+    method public void setOcspResponderCert(java.security.cert.X509Certificate);
+    method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate, byte[]>);
+    method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>);
+  }
+
+  public static final class PKIXRevocationChecker.Option extends java.lang.Enum {
+    method public static java.security.cert.PKIXRevocationChecker.Option valueOf(java.lang.String);
+    method public static final java.security.cert.PKIXRevocationChecker.Option[] values();
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL;
+  }
+
   public abstract interface PolicyNode {
     method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren();
     method public abstract int getDepth();
@@ -57167,6 +57218,7 @@
     method public javax.security.auth.x500.X500Principal getSubjectX500Principal();
     method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException;
     method public abstract int getVersion();
+    method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
   }
 
   public abstract interface X509Extension {
diff --git a/api/test-current.txt b/api/test-current.txt
index 13d98f4..21b101f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5795,6 +5795,7 @@
     method public boolean getCameraDisabled(android.content.ComponentName);
     method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
+    method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
     method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
     method public java.lang.String getDeviceOwnerLockScreenInfo();
@@ -5859,6 +5860,7 @@
     method public void setCameraDisabled(android.content.ComponentName, boolean);
     method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
     method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+    method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
     method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
@@ -12792,6 +12794,7 @@
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
     field public static final int PADDING_MODE_NEST = 0; // 0x0
     field public static final int PADDING_MODE_STACK = 1; // 0x1
+    field public static final int UNDEFINED_INSET = -2147483648; // 0x80000000
   }
 
   public class LevelListDrawable extends android.graphics.drawable.DrawableContainer {
@@ -20376,8 +20379,6 @@
     field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
     field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
     field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
-    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
-    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
     field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
@@ -22587,7 +22588,16 @@
 
   public class MtpEvent {
     ctor public MtpEvent();
+    method public int getDevicePropCode();
     method public int getEventCode();
+    method public int getObjectFormatCode();
+    method public int getObjectHandle();
+    method public int getObjectPropCode();
+    method public int getParameter1();
+    method public int getParameter2();
+    method public int getParameter3();
+    method public int getStorageId();
+    method public int getTransactionId();
   }
 
   public final class MtpObjectInfo {
@@ -49558,6 +49568,8 @@
   public class InternalError extends java.lang.VirtualMachineError {
     ctor public InternalError();
     ctor public InternalError(java.lang.String);
+    ctor public InternalError(java.lang.String, java.lang.Throwable);
+    ctor public InternalError(java.lang.Throwable);
   }
 
   public class InterruptedException extends java.lang.Exception {
@@ -50330,6 +50342,8 @@
   public abstract class VirtualMachineError extends java.lang.Error {
     ctor public VirtualMachineError();
     ctor public VirtualMachineError(java.lang.String);
+    ctor public VirtualMachineError(java.lang.String, java.lang.Throwable);
+    ctor public VirtualMachineError(java.lang.Throwable);
   }
 
   public final class Void {
@@ -54072,6 +54086,7 @@
     method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
   }
 
   public class CertPathBuilderException extends java.security.GeneralSecurityException {
@@ -54089,6 +54104,13 @@
   public abstract class CertPathBuilderSpi {
     ctor public CertPathBuilderSpi();
     method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException;
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
+  }
+
+  public abstract interface CertPathChecker {
+    method public abstract void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
+    method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
+    method public abstract boolean isForwardCheckingSupported();
   }
 
   public abstract interface CertPathParameters implements java.lang.Cloneable {
@@ -54103,6 +54125,7 @@
     method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
     method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
   }
 
@@ -54139,6 +54162,7 @@
 
   public abstract class CertPathValidatorSpi {
     ctor public CertPathValidatorSpi();
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
     method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
   }
 
@@ -54297,9 +54321,10 @@
     method public java.security.cert.CertPath getCertPath();
   }
 
-  public abstract class PKIXCertPathChecker implements java.lang.Cloneable {
+  public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable {
     ctor protected PKIXCertPathChecker();
     method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException;
+    method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
     method public java.lang.Object clone();
     method public abstract java.util.Set<java.lang.String> getSupportedExtensions();
     method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
@@ -54359,6 +54384,30 @@
     enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT;
   }
 
+  public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker {
+    ctor protected PKIXRevocationChecker();
+    method public java.util.List<java.security.cert.Extension> getOcspExtensions();
+    method public java.net.URI getOcspResponder();
+    method public java.security.cert.X509Certificate getOcspResponderCert();
+    method public java.util.Map<java.security.cert.X509Certificate, byte[]> getOcspResponses();
+    method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions();
+    method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions();
+    method public void setOcspExtensions(java.util.List<java.security.cert.Extension>);
+    method public void setOcspResponder(java.net.URI);
+    method public void setOcspResponderCert(java.security.cert.X509Certificate);
+    method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate, byte[]>);
+    method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>);
+  }
+
+  public static final class PKIXRevocationChecker.Option extends java.lang.Enum {
+    method public static java.security.cert.PKIXRevocationChecker.Option valueOf(java.lang.String);
+    method public static final java.security.cert.PKIXRevocationChecker.Option[] values();
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL;
+  }
+
   public abstract interface PolicyNode {
     method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren();
     method public abstract int getDepth();
@@ -54518,6 +54567,7 @@
     method public javax.security.auth.x500.X500Principal getSubjectX500Principal();
     method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException;
     method public abstract int getVersion();
+    method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
   }
 
   public abstract interface X509Extension {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 81e00ff..4e55c89 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2015,7 +2015,7 @@
     private static final String ONE_COUNT_COLUMN_HEADER = "%21s %8s";
 
     // Formatting for checkin service - update version if row format changes
-    private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3;
+    private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 4;
 
     static void printRow(PrintWriter pw, String format, Object...objs) {
         pw.println(String.format(format, objs));
@@ -2091,6 +2091,25 @@
             pw.print(memInfo.otherPrivateClean); pw.print(',');
             pw.print(memInfo.getTotalPrivateClean()); pw.print(',');
 
+            // Heap info - swapped out
+            pw.print(memInfo.nativeSwappedOut); pw.print(',');
+            pw.print(memInfo.dalvikSwappedOut); pw.print(',');
+            pw.print(memInfo.otherSwappedOut); pw.print(',');
+            pw.print(memInfo.getTotalSwappedOut()); pw.print(',');
+
+            // Heap info - swapped out pss
+            if (memInfo.hasSwappedOutPss) {
+                pw.print(memInfo.nativeSwappedOutPss); pw.print(',');
+                pw.print(memInfo.dalvikSwappedOutPss); pw.print(',');
+                pw.print(memInfo.otherSwappedOutPss); pw.print(',');
+                pw.print(memInfo.getTotalSwappedOutPss()); pw.print(',');
+            } else {
+                pw.print("N/A,");
+                pw.print("N/A,");
+                pw.print("N/A,");
+                pw.print("N/A,");
+            }
+
             // Heap info - other areas
             for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
                 pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(',');
@@ -2100,6 +2119,12 @@
                 pw.print(memInfo.getOtherSharedClean(i)); pw.print(',');
                 pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(',');
                 pw.print(memInfo.getOtherPrivateClean(i)); pw.print(',');
+                pw.print(memInfo.getOtherSwappedOut(i)); pw.print(',');
+                if (memInfo.hasSwappedOutPss) {
+                    pw.print(memInfo.getOtherSwappedOutPss(i)); pw.print(',');
+                } else {
+                    pw.print("N/A,");
+                }
             }
             return;
         }
@@ -2107,35 +2132,44 @@
         if (!dumpSummaryOnly) {
             if (dumpFullInfo) {
                 printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private",
-                        "Shared", "Private", "Swapped", "Heap", "Heap", "Heap");
+                        "Shared", "Private", memInfo.hasSwappedOutPss ? "SwapPss" : "Swap",
+                        "Heap", "Heap", "Heap");
                 printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty",
-                        "Clean", "Clean", "Dirty", "Size", "Alloc", "Free");
+                        "Clean", "Clean", "Dirty",
+                        "Size", "Alloc", "Free");
                 printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------",
                         "------", "------", "------", "------", "------", "------");
                 printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss,
                         memInfo.nativeSwappablePss, memInfo.nativeSharedDirty,
                         memInfo.nativePrivateDirty, memInfo.nativeSharedClean,
-                        memInfo.nativePrivateClean, memInfo.nativeSwappedOut,
+                        memInfo.nativePrivateClean, memInfo.hasSwappedOutPss ?
+                        memInfo.nativeSwappedOut : memInfo.nativeSwappedOutPss,
                         nativeMax, nativeAllocated, nativeFree);
                 printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss,
                         memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty,
                         memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean,
-                        memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut,
+                        memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss ?
+                        memInfo.dalvikSwappedOut : memInfo.dalvikSwappedOutPss,
                         dalvikMax, dalvikAllocated, dalvikFree);
             } else {
                 printRow(pw, HEAP_COLUMN, "", "Pss", "Private",
-                        "Private", "Swapped", "Heap", "Heap", "Heap");
+                        "Private", memInfo.hasSwappedOutPss ? "SwapPss" : "Swap",
+                        "Heap", "Heap", "Heap");
                 printRow(pw, HEAP_COLUMN, "", "Total", "Dirty",
                         "Clean", "Dirty", "Size", "Alloc", "Free");
                 printRow(pw, HEAP_COLUMN, "", "------", "------", "------",
                         "------", "------", "------", "------", "------");
                 printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss,
                         memInfo.nativePrivateDirty,
-                        memInfo.nativePrivateClean, memInfo.nativeSwappedOut,
+                        memInfo.nativePrivateClean,
+                        memInfo.hasSwappedOutPss ? memInfo.nativeSwappedOutPss :
+                        memInfo.nativeSwappedOut,
                         nativeMax, nativeAllocated, nativeFree);
                 printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss,
                         memInfo.dalvikPrivateDirty,
-                        memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut,
+                        memInfo.dalvikPrivateClean,
+                        memInfo.hasSwappedOutPss ? memInfo.dalvikSwappedOutPss :
+                        memInfo.dalvikSwappedOut,
                         dalvikMax, dalvikAllocated, dalvikFree);
             }
 
@@ -2146,6 +2180,7 @@
             int otherSharedClean = memInfo.otherSharedClean;
             int otherPrivateClean = memInfo.otherPrivateClean;
             int otherSwappedOut = memInfo.otherSwappedOut;
+            int otherSwappedOutPss = memInfo.otherSwappedOutPss;
 
             for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
                 final int myPss = memInfo.getOtherPss(i);
@@ -2155,16 +2190,22 @@
                 final int mySharedClean = memInfo.getOtherSharedClean(i);
                 final int myPrivateClean = memInfo.getOtherPrivateClean(i);
                 final int mySwappedOut = memInfo.getOtherSwappedOut(i);
+                final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i);
                 if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
-                        || mySharedClean != 0 || myPrivateClean != 0 || mySwappedOut != 0) {
+                        || mySharedClean != 0 || myPrivateClean != 0
+                        || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
                     if (dumpFullInfo) {
                         printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
                                 myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
-                                mySharedClean, myPrivateClean, mySwappedOut, "", "", "");
+                                mySharedClean, myPrivateClean,
+                                memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
+                                "", "", "");
                     } else {
                         printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
                                 myPss, myPrivateDirty,
-                                myPrivateClean, mySwappedOut, "", "", "");
+                                myPrivateClean,
+                                memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
+                                "", "", "");
                     }
                     otherPss -= myPss;
                     otherSwappablePss -= mySwappablePss;
@@ -2173,26 +2214,32 @@
                     otherSharedClean -= mySharedClean;
                     otherPrivateClean -= myPrivateClean;
                     otherSwappedOut -= mySwappedOut;
+                    otherSwappedOutPss -= mySwappedOutPss;
                 }
             }
 
             if (dumpFullInfo) {
                 printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss,
                         otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean,
-                        otherSwappedOut, "", "", "");
+                        memInfo.hasSwappedOutPss ? otherSwappedOutPss : otherSwappedOut,
+                        "", "", "");
                 printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(),
                         memInfo.getTotalSwappablePss(),
                         memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
                         memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
-                        memInfo.getTotalSwappedOut(), nativeMax+dalvikMax,
-                        nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
+                        memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOut() :
+                        memInfo.getTotalSwappedOutPss(),
+                        nativeMax+dalvikMax, nativeAllocated+dalvikAllocated,
+                        nativeFree+dalvikFree);
             } else {
                 printRow(pw, HEAP_COLUMN, "Unknown", otherPss,
-                        otherPrivateDirty, otherPrivateClean, otherSwappedOut,
+                        otherPrivateDirty, otherPrivateClean,
+                        memInfo.hasSwappedOutPss ? otherSwappedOutPss : otherSwappedOut,
                         "", "", "");
                 printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(),
                         memInfo.getTotalPrivateDirty(),
                         memInfo.getTotalPrivateClean(),
+                        memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() :
                         memInfo.getTotalSwappedOut(),
                         nativeMax+dalvikMax,
                         nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
@@ -2211,16 +2258,22 @@
                     final int mySharedClean = memInfo.getOtherSharedClean(i);
                     final int myPrivateClean = memInfo.getOtherPrivateClean(i);
                     final int mySwappedOut = memInfo.getOtherSwappedOut(i);
+                    final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i);
                     if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
-                            || mySharedClean != 0 || myPrivateClean != 0) {
+                            || mySharedClean != 0 || myPrivateClean != 0
+                            || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
                         if (dumpFullInfo) {
                             printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
                                     myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
-                                    mySharedClean, myPrivateClean, mySwappedOut, "", "", "");
+                                    mySharedClean, myPrivateClean,
+                                    memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
+                                    "", "", "");
                         } else {
                             printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
                                     myPss, myPrivateDirty,
-                                    myPrivateClean, mySwappedOut, "", "", "");
+                                    myPrivateClean,
+                                    memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
+                                    "", "", "");
                         }
                     }
                 }
@@ -2246,9 +2299,15 @@
         printRow(pw, ONE_COUNT_COLUMN,
             "System:", memInfo.getSummarySystem());
         pw.println(" ");
-        printRow(pw, TWO_COUNT_COLUMNS,
-            "TOTAL:", memInfo.getSummaryTotalPss(),
-            "TOTAL SWAP (KB):", memInfo.getSummaryTotalSwap());
+        if (memInfo.hasSwappedOutPss) {
+            printRow(pw, TWO_COUNT_COLUMNS,
+                "TOTAL:", memInfo.getSummaryTotalPss(),
+                "TOTAL SWAP PSS:", memInfo.getSummaryTotalSwapPss());
+        } else {
+            printRow(pw, TWO_COUNT_COLUMNS,
+                "TOTAL:", memInfo.getSummaryTotalPss(),
+                "TOTAL SWAP (KB):", memInfo.getSummaryTotalSwap());
+        }
     }
 
     public void registerOnActivityPausedListener(Activity activity,
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index a73ad09..e163b1c 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -409,9 +409,6 @@
     // If set this fragment is being removed from its activity.
     boolean mRemoving;
 
-    // True if the fragment is in the resumed state.
-    boolean mResumed;
-
     // Set to true if this fragment was instantiated from a layout file.
     boolean mFromLayout;
 
@@ -928,7 +925,7 @@
      * for the duration of {@link #onResume()} and {@link #onPause()} as well.
      */
     final public boolean isResumed() {
-        return mResumed;
+        return mState >= RESUMED;
     }
 
     /**
@@ -1630,7 +1627,6 @@
         mWho = null;
         mAdded = false;
         mRemoving = false;
-        mResumed = false;
         mFromLayout = false;
         mInLayout = false;
         mRestored = false;
@@ -2113,7 +2109,6 @@
         writer.print(" mBackStackNesting="); writer.println(mBackStackNesting);
         writer.print(prefix); writer.print("mAdded="); writer.print(mAdded);
         writer.print(" mRemoving="); writer.print(mRemoving);
-        writer.print(" mResumed="); writer.print(mResumed);
         writer.print(" mFromLayout="); writer.print(mFromLayout);
         writer.print(" mInLayout="); writer.println(mInLayout);
         writer.print(prefix); writer.print("mHidden="); writer.print(mHidden);
@@ -2208,6 +2203,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.noteStateNotSaved();
         }
+        mState = CREATED;
         mCalled = false;
         onCreate(savedInstanceState);
         if (!mCalled) {
@@ -2238,6 +2234,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.noteStateNotSaved();
         }
+        mState = ACTIVITY_CREATED;
         mCalled = false;
         onActivityCreated(savedInstanceState);
         if (!mCalled) {
@@ -2254,6 +2251,7 @@
             mChildFragmentManager.noteStateNotSaved();
             mChildFragmentManager.execPendingActions();
         }
+        mState = STARTED;
         mCalled = false;
         onStart();
         if (!mCalled) {
@@ -2273,6 +2271,7 @@
             mChildFragmentManager.noteStateNotSaved();
             mChildFragmentManager.execPendingActions();
         }
+        mState = RESUMED;
         mCalled = false;
         onResume();
         if (!mCalled) {
@@ -2389,6 +2388,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.dispatchPause();
         }
+        mState = STARTED;
         mCalled = false;
         onPause();
         if (!mCalled) {
@@ -2401,6 +2401,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.dispatchStop();
         }
+        mState = STOPPED;
         mCalled = false;
         onStop();
         if (!mCalled) {
@@ -2428,6 +2429,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.dispatchDestroyView();
         }
+        mState = CREATED;
         mCalled = false;
         onDestroyView();
         if (!mCalled) {
@@ -2443,6 +2445,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.dispatchDestroy();
         }
+        mState = INITIALIZING;
         mCalled = false;
         onDestroy();
         if (!mCalled) {
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 696ccdb..84ae09d 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1004,7 +1004,6 @@
                 case Fragment.STARTED:
                     if (newState > Fragment.STARTED) {
                         if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
-                        f.mResumed = true;
                         f.performResume();
                         // Get rid of this in case we saved it and never needed it.
                         f.mSavedFragmentState = null;
@@ -1017,7 +1016,6 @@
                     if (newState < Fragment.RESUMED) {
                         if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
                         f.performPause();
-                        f.mResumed = false;
                     }
                 case Fragment.STARTED:
                     if (newState < Fragment.STARTED) {
@@ -1096,6 +1094,8 @@
                             if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
                             if (!f.mRetaining) {
                                 f.performDestroy();
+                            } else {
+                                f.mState = Fragment.INITIALIZING;
                             }
 
                             f.mCalled = false;
@@ -1119,7 +1119,11 @@
             }
         }
         
-        f.mState = newState;
+        if (f.mState != newState) {
+            Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
+                    + "expected state " + newState + " found " + f.mState);
+            f.mState = newState;
+        }
     }
     
     void moveToState(Fragment f) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 08e9b1e..4de3ceb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3540,6 +3540,66 @@
     }
 
     /**
+     * Called by a profile owner of a managed profile to set whether contacts search from
+     * the managed profile will be shown in the parent profile, for incoming calls.
+     *
+     * <p>The calling device admin must be a profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param disabled If true contacts search in the managed profile is not displayed.
+     */
+    public void setCrossProfileContactsSearchDisabled(@NonNull ComponentName admin,
+            boolean disabled) {
+        if (mService != null) {
+            try {
+                mService.setCrossProfileContactsSearchDisabled(admin, disabled);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+    }
+
+    /**
+     * Called by a profile owner of a managed profile to determine whether or not contacts search
+     * has been disabled.
+     *
+     * <p>The calling device admin must be a profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     */
+    public boolean getCrossProfileContactsSearchDisabled(@NonNull ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getCrossProfileContactsSearchDisabled(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Determine whether or not contacts search has been disabled.
+     *
+     * @param userHandle The user for whom to check the contacts search permission
+     * @hide
+     */
+    public boolean getCrossProfileContactsSearchDisabled(@NonNull UserHandle userHandle) {
+        if (mService != null) {
+            try {
+                return mService
+                        .getCrossProfileContactsSearchDisabledForUser(userHandle.getIdentifier());
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return false;
+    }
+
+    /**
      * Start Quick Contact on the managed profile for the user, if the policy allows.
      * @hide
      */
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 754cb43..d3c32c5 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -202,6 +202,9 @@
     void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled);
     boolean getCrossProfileCallerIdDisabled(in ComponentName who);
     boolean getCrossProfileCallerIdDisabledForUser(int userId);
+    void setCrossProfileContactsSearchDisabled(in ComponentName who, boolean disabled);
+    boolean getCrossProfileContactsSearchDisabled(in ComponentName who);
+    boolean getCrossProfileContactsSearchDisabledForUser(int userId);
     void startManagedQuickContact(String lookupKey, long contactId, long directoryId, in Intent originalIntent);
 
     void setBluetoothContactSharingDisabled(in ComponentName who, boolean disabled);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 38abac7..5113e19 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4413,6 +4413,15 @@
         final PackageParser parser = new PackageParser();
         final File apkFile = new File(archiveFilePath);
         try {
+            if ((flags & (MATCH_ENCRYPTION_UNAWARE | MATCH_ENCRYPTION_AWARE)) != 0) {
+                // Caller expressed an explicit opinion about what encryption
+                // aware/unaware components they want to see, so fall through and
+                // give them what they want
+            } else {
+                // Caller expressed no opinion, so match everything
+                flags |= MATCH_ENCRYPTION_AWARE_AND_UNAWARE;
+            }
+
             PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
             if ((flags & GET_SIGNATURES) != 0) {
                 parser.collectCertificates(pkg, 0);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 1349662..a0df610 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -463,92 +463,60 @@
                 p.featureGroups.toArray(pi.featureGroups);
             }
         }
-        if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
-            int N = p.activities.size();
+        if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
+            final int N = p.activities.size();
             if (N > 0) {
-                if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                    pi.activities = new ActivityInfo[N];
-                } else {
-                    int num = 0;
-                    for (int i=0; i<N; i++) {
-                        if (p.activities.get(i).info.enabled) num++;
-                    }
-                    pi.activities = new ActivityInfo[num];
-                }
-                for (int i=0, j=0; i<N; i++) {
-                    final Activity activity = p.activities.get(i);
-                    if (activity.info.enabled
-                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
-                                state, userId);
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Activity a = p.activities.get(i);
+                    if (state.isMatch(a.info, flags)) {
+                        res[num++] = generateActivityInfo(a, flags, state, userId);
                     }
                 }
+                pi.activities = ArrayUtils.trimToSize(res, num);
             }
         }
-        if ((flags&PackageManager.GET_RECEIVERS) != 0) {
-            int N = p.receivers.size();
+        if ((flags & PackageManager.GET_RECEIVERS) != 0) {
+            final int N = p.receivers.size();
             if (N > 0) {
-                if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                    pi.receivers = new ActivityInfo[N];
-                } else {
-                    int num = 0;
-                    for (int i=0; i<N; i++) {
-                        if (p.receivers.get(i).info.enabled) num++;
-                    }
-                    pi.receivers = new ActivityInfo[num];
-                }
-                for (int i=0, j=0; i<N; i++) {
-                    final Activity activity = p.receivers.get(i);
-                    if (activity.info.enabled
-                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
-                                state, userId);
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Activity a = p.receivers.get(i);
+                    if (state.isMatch(a.info, flags)) {
+                        res[num++] = generateActivityInfo(a, flags, state, userId);
                     }
                 }
+                pi.receivers = ArrayUtils.trimToSize(res, num);
             }
         }
-        if ((flags&PackageManager.GET_SERVICES) != 0) {
-            int N = p.services.size();
+        if ((flags & PackageManager.GET_SERVICES) != 0) {
+            final int N = p.services.size();
             if (N > 0) {
-                if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                    pi.services = new ServiceInfo[N];
-                } else {
-                    int num = 0;
-                    for (int i=0; i<N; i++) {
-                        if (p.services.get(i).info.enabled) num++;
-                    }
-                    pi.services = new ServiceInfo[num];
-                }
-                for (int i=0, j=0; i<N; i++) {
-                    final Service service = p.services.get(i);
-                    if (service.info.enabled
-                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.services[j++] = generateServiceInfo(p.services.get(i), flags,
-                                state, userId);
+                int num = 0;
+                final ServiceInfo[] res = new ServiceInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Service s = p.services.get(i);
+                    if (state.isMatch(s.info, flags)) {
+                        res[num++] = generateServiceInfo(s, flags, state, userId);
                     }
                 }
+                pi.services = ArrayUtils.trimToSize(res, num);
             }
         }
-        if ((flags&PackageManager.GET_PROVIDERS) != 0) {
-            int N = p.providers.size();
+        if ((flags & PackageManager.GET_PROVIDERS) != 0) {
+            final int N = p.providers.size();
             if (N > 0) {
-                if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                    pi.providers = new ProviderInfo[N];
-                } else {
-                    int num = 0;
-                    for (int i=0; i<N; i++) {
-                        if (p.providers.get(i).info.enabled) num++;
-                    }
-                    pi.providers = new ProviderInfo[num];
-                }
-                for (int i=0, j=0; i<N; i++) {
-                    final Provider provider = p.providers.get(i);
-                    if (provider.info.enabled
-                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags,
-                                state, userId);
+                int num = 0;
+                final ProviderInfo[] res = new ProviderInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Provider pr = p.providers.get(i);
+                    if (state.isMatch(pr.info, flags)) {
+                        res[num++] = generateProviderInfo(pr, flags, state, userId);
                     }
                 }
+                pi.providers = ArrayUtils.trimToSize(res, num);
             }
         }
         if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) {
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 91fdf7f..38e0044 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -17,9 +17,20 @@
 package android.content.pm;
 
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_AWARE;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 
 import android.util.ArraySet;
 
+import com.android.internal.util.ArrayUtils;
+
 /**
  * Per-user state information about a package.
  * @hide
@@ -58,12 +69,77 @@
         hidden = o.hidden;
         suspended = o.suspended;
         lastDisableAppCaller = o.lastDisableAppCaller;
-        disabledComponents = o.disabledComponents != null
-                ? new ArraySet<>(o.disabledComponents) : null;
-        enabledComponents = o.enabledComponents != null
-                ? new ArraySet<>(o.enabledComponents) : null;
+        disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
+        enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
         blockUninstall = o.blockUninstall;
         domainVerificationStatus = o.domainVerificationStatus;
         appLinkGeneration = o.appLinkGeneration;
     }
+
+    /**
+     * Test if this package is installed.
+     */
+    public boolean isInstalled(int flags) {
+        return (this.installed && !this.hidden)
+                || (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0;
+    }
+
+    /**
+     * Test if the given component is considered installed, enabled and a match
+     * for the given flags.
+     */
+    public boolean isMatch(ComponentInfo componentInfo, int flags) {
+        if (!isInstalled(flags)) return false;
+        if (!isEnabled(componentInfo, flags)) return false;
+
+        if ((flags & MATCH_SYSTEM_ONLY) != 0) {
+            if (!componentInfo.applicationInfo.isSystemApp()) {
+                return false;
+            }
+        }
+
+        final boolean matchesUnaware = ((flags & MATCH_ENCRYPTION_UNAWARE) != 0)
+                && !componentInfo.encryptionAware;
+        final boolean matchesAware = ((flags & MATCH_ENCRYPTION_AWARE) != 0)
+                && componentInfo.encryptionAware;
+        return matchesUnaware || matchesAware;
+    }
+
+    /**
+     * Test if the given component is considered enabled.
+     */
+    public boolean isEnabled(ComponentInfo componentInfo, int flags) {
+        if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
+            return true;
+        }
+
+        // First check if the overall package is disabled; if the package is
+        // enabled then fall through to check specific component
+        switch (this.enabled) {
+            case COMPONENT_ENABLED_STATE_DISABLED:
+            case COMPONENT_ENABLED_STATE_DISABLED_USER:
+                return false;
+            case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+                if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) {
+                    return false;
+                }
+            case COMPONENT_ENABLED_STATE_DEFAULT:
+                if (!componentInfo.applicationInfo.enabled) {
+                    return false;
+                }
+            case COMPONENT_ENABLED_STATE_ENABLED:
+                break;
+        }
+
+        // Check if component has explicit state before falling through to
+        // the manifest default
+        if (ArrayUtils.contains(this.enabledComponents, componentInfo.name)) {
+            return true;
+        }
+        if (ArrayUtils.contains(this.disabledComponents, componentInfo.name)) {
+            return false;
+        }
+
+        return componentInfo.enabled;
+    }
 }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f642f08..2826882 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1258,6 +1258,10 @@
      */
     @CallSuper
     public boolean onEvaluateInputViewShown() {
+        if (mSettingsObserver == null) {
+            Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here.");
+            return false;
+        }
         if (mSettingsObserver.shouldShowImeWithHardKeyboard()) {
             return true;
         }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index cabc6fa..176e923 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -19,6 +19,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -897,6 +898,24 @@
     }
 
     /**
+     * Gets the URL that should be used for resolving whether a captive portal is present.
+     * 1. This URL should respond with a 204 response to a GET request to indicate no captive
+     *    portal is present.
+     * 2. This URL must be HTTP as redirect responses are used to find captive portal
+     *    sign-in pages. Captive portals cannot respond to HTTPS requests with redirects.
+     *
+     * @hide
+     */
+    @SystemApi
+    public String getCaptivePortalServerUrl() {
+        try {
+            return mService.getCaptivePortalServerUrl();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Tells the underlying networking system that the caller wants to
      * begin using the named feature. The interpretation of {@code feature}
      * is completely up to each networking implementation.
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d4dd669..ef91137 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -165,4 +165,6 @@
             in IBinder binder, String srcAddr, int srcPort, String dstAddr);
 
     void stopKeepalive(in Network network, int slot);
+
+    String getCaptivePortalServerUrl();
 }
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index fdd34f5..e58744b 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -130,6 +130,9 @@
         /** The dirty dalvik pages that have been swapped out. */
         /** @hide We may want to expose this, eventually. */
         public int dalvikSwappedOut;
+        /** The dirty dalvik pages that have been swapped out, proportional. */
+        /** @hide We may want to expose this, eventually. */
+        public int dalvikSwappedOutPss;
 
         /** The proportional set size for the native heap. */
         public int nativePss;
@@ -149,6 +152,9 @@
         /** The dirty native pages that have been swapped out. */
         /** @hide We may want to expose this, eventually. */
         public int nativeSwappedOut;
+        /** The dirty native pages that have been swapped out, proportional. */
+        /** @hide We may want to expose this, eventually. */
+        public int nativeSwappedOutPss;
 
         /** The proportional set size for everything else. */
         public int otherPss;
@@ -168,6 +174,13 @@
         /** The dirty pages used by anyting else that have been swapped out. */
         /** @hide We may want to expose this, eventually. */
         public int otherSwappedOut;
+        /** The dirty pages used by anyting else that have been swapped out, proportional. */
+        /** @hide We may want to expose this, eventually. */
+        public int otherSwappedOutPss;
+
+        /** Whether the kernel reports proportional swap usage */
+        /** @hide */
+        public boolean hasSwappedOutPss;
 
         /** @hide */
         public static final int HEAP_UNKNOWN = 0;
@@ -235,7 +248,7 @@
         public static final int NUM_DVK_STATS = 8;
 
         /** @hide */
-        public static final int NUM_CATEGORIES = 7;
+        public static final int NUM_CATEGORIES = 8;
 
         /** @hide */
         public static final int offsetPss = 0;
@@ -251,6 +264,8 @@
         public static final int offsetSharedClean = 5;
         /** @hide */
         public static final int offsetSwappedOut = 6;
+        /** @hide */
+        public static final int offsetSwappedOutPss = 7;
 
         private int[] otherStats = new int[(NUM_OTHER_STATS+NUM_DVK_STATS)*NUM_CATEGORIES];
 
@@ -261,7 +276,7 @@
          * Return total PSS memory usage in kB.
          */
         public int getTotalPss() {
-            return dalvikPss + nativePss + otherPss;
+            return dalvikPss + nativePss + otherPss + getTotalSwappedOutPss();
         }
 
         /**
@@ -274,7 +289,8 @@
         }
 
         /**
-         * Return total PSS memory usage in kB.
+         * Return total PSS memory usage in kB mapping a file of one of the following extension:
+         * .so, .jar, .apk, .ttf, .dex, .odex, .oat, .art .
          */
         public int getTotalSwappablePss() {
             return dalvikSwappablePss + nativeSwappablePss + otherSwappablePss;
@@ -316,6 +332,14 @@
             return dalvikSwappedOut + nativeSwappedOut + otherSwappedOut;
         }
 
+        /**
+         * Return total swapped out memory in kB, proportional.
+         * @hide
+         */
+        public int getTotalSwappedOutPss() {
+            return dalvikSwappedOutPss + nativeSwappedOutPss + otherSwappedOutPss;
+        }
+
         /** @hide */
         public int getOtherPss(int which) {
             return otherStats[which*NUM_CATEGORIES + offsetPss];
@@ -359,6 +383,11 @@
         }
 
         /** @hide */
+        public int getOtherSwappedOutPss(int which) {
+            return otherStats[which*NUM_CATEGORIES + offsetSwappedOutPss];
+        }
+
+        /** @hide */
         public static String getOtherLabel(int which) {
             switch (which) {
                 case OTHER_DALVIK_OTHER: return "Dalvik Other";
@@ -632,12 +661,24 @@
          *    know if the Swap memory is shared or private, so we don't know
          *    what to blame on the application and what on the system.
          *    For now, just lump all the Swap in one place.
+         *    For kernels reporting SwapPss {@link #getSummaryTotalSwapPss()}
+         *    will report the application proportional Swap.
          * @hide
          */
         public int getSummaryTotalSwap() {
             return getTotalSwappedOut();
         }
 
+        /**
+         * Total proportional Swap in KB.
+         * Notes:
+         *  * Always 0 if {@link #hasSwappedOutPss} is false.
+         * @hide
+         */
+        public int getSummaryTotalSwapPss() {
+            return getTotalSwappedOutPss();
+        }
+
         public int describeContents() {
             return 0;
         }
@@ -664,6 +705,8 @@
             dest.writeInt(otherPrivateClean);
             dest.writeInt(otherSharedClean);
             dest.writeInt(otherSwappedOut);
+            dest.writeInt(hasSwappedOutPss ? 1 : 0);
+            dest.writeInt(otherSwappedOutPss);
             dest.writeIntArray(otherStats);
         }
 
@@ -689,6 +732,8 @@
             otherPrivateClean = source.readInt();
             otherSharedClean = source.readInt();
             otherSwappedOut = source.readInt();
+            hasSwappedOutPss = source.readInt() != 0;
+            otherSwappedOutPss = source.readInt();
             otherStats = source.createIntArray();
         }
 
@@ -1563,11 +1608,12 @@
 
     /**
      * Retrieves the PSS memory used by the process as given by the
-     * smaps.  Optionally supply a long array of 1 entry to also
-     * receive the uss of the process, and another array to also
-     * retrieve the separate memtrack size.  @hide
+     * smaps.  Optionally supply a long array of 2 entries to also
+     * receive the Uss and SwapPss of the process, and another array to also
+     * retrieve the separate memtrack size.
+     * @hide
      */
-    public static native long getPss(int pid, long[] outUss, long[] outMemtrack);
+    public static native long getPss(int pid, long[] outUssSwapPss, long[] outMemtrack);
 
     /** @hide */
     public static final int MEMINFO_TOTAL = 0;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d4c9944..a6485e4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -641,7 +641,7 @@
      * provided by the platform for applications to operate correctly in the various power
      * saving mode.  This is only for unusual applications that need to deeply control their own
      * execution, at the potential expense of the user's battery life.  Note that these applications
-     * greatly run the risk of showing to the user has how power consumers on their device.</p>
+     * greatly run the risk of showing to the user as high power consumers on their device.</p>
      * <p>
      * Input: The Intent's data URI must specify the application package name
      * to be shown, with the "package" scheme.  That is "package:com.my.app".
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1c24392..f674298 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5359,6 +5359,9 @@
     void offsetRectBetweenParentAndChild(View descendant, Rect rect,
             boolean offsetFromChildToParent, boolean clipToBounds) {
 
+        final RectF rectF = mAttachInfo != null ? mAttachInfo.mTmpTransformRect1 : new RectF();
+        final Matrix inverse = mAttachInfo != null ? mAttachInfo.mTmpMatrix : new Matrix();
+
         // already in the same coord system :)
         if (descendant == this) {
             return;
@@ -5372,8 +5375,16 @@
                 && (theParent != this)) {
 
             if (offsetFromChildToParent) {
-                rect.offset(descendant.mLeft - descendant.mScrollX,
-                        descendant.mTop - descendant.mScrollY);
+                rect.offset(-descendant.mScrollX, -descendant.mScrollY);
+
+                if (!descendant.hasIdentityMatrix()) {
+                    rectF.set(rect);
+                    descendant.getMatrix().mapRect(rectF);
+                    rectF.roundOut(rect);
+                }
+
+                rect.offset(descendant.mLeft, descendant.mTop);
+
                 if (clipToBounds) {
                     View p = (View) theParent;
                     boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
@@ -5391,8 +5402,16 @@
                         rect.setEmpty();
                     }
                 }
-                rect.offset(descendant.mScrollX - descendant.mLeft,
-                        descendant.mScrollY - descendant.mTop);
+                rect.offset(-descendant.mLeft, -descendant.mTop);
+
+                if (!descendant.hasIdentityMatrix()) {
+                    descendant.getMatrix().invert(inverse);
+                    rectF.set(rect);
+                    inverse.mapRect(rectF);
+                    rectF.roundOut(rect);
+                }
+
+                rect.offset(descendant.mScrollX, descendant.mScrollY);
             }
 
             descendant = (View) theParent;
@@ -5403,11 +5422,26 @@
         // to get into our coordinate space
         if (theParent == this) {
             if (offsetFromChildToParent) {
-                rect.offset(descendant.mLeft - descendant.mScrollX,
-                        descendant.mTop - descendant.mScrollY);
+                rect.offset(-descendant.mScrollX, -descendant.mScrollY);
+
+                if (!descendant.hasIdentityMatrix()) {
+                    rectF.set(rect);
+                    descendant.getMatrix().mapRect(rectF);
+                    rectF.roundOut(rect);
+                }
+
+                rect.offset(descendant.mLeft, descendant.mTop);
             } else {
-                rect.offset(descendant.mScrollX - descendant.mLeft,
-                        descendant.mScrollY - descendant.mTop);
+                rect.offset(-descendant.mLeft, -descendant.mTop);
+
+                if (!descendant.hasIdentityMatrix()) {
+                    descendant.getMatrix().invert(inverse);
+                    rectF.set(rect);
+                    inverse.mapRect(rectF);
+                    rectF.roundOut(rect);
+                }
+
+                rect.offset(descendant.mScrollX, descendant.mScrollY);
             }
         } else {
             throw new IllegalArgumentException("parameter must be a descendant of this view");
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0fb3951..3eb2e37 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1488,7 +1488,8 @@
                 if ((lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT)
                         && (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
-                                || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD)) {
+                                || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD
+                                || lp.type == WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)) {
                     windowSizeMayChange = true;
                     // NOTE -- system code, won't try to do compat mode.
                     Point size = new Point();
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index c79e184..09cf704 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -271,6 +271,10 @@
                         attrs, R.styleable.Spinner, defStyleAttr, defStyleRes);
                 mDropDownWidth = pa.getLayoutDimension(R.styleable.Spinner_dropDownWidth,
                         ViewGroup.LayoutParams.WRAP_CONTENT);
+                if (pa.hasValueOrEmpty(R.styleable.Spinner_dropDownSelector)) {
+                    popup.setListSelector(pa.getDrawable(
+                            R.styleable.Spinner_dropDownSelector));
+                }
                 popup.setBackgroundDrawable(pa.getDrawable(R.styleable.Spinner_popupBackground));
                 popup.setPromptText(a.getString(R.styleable.Spinner_prompt));
                 pa.recycle();
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index 830da79..b3222f0 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -19,6 +19,7 @@
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.os.SystemClock;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.util.Preconditions;
@@ -29,6 +30,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Arrays;
 
 /**
  * Represents a connection to {@code installd}. Allows multiple connect and
@@ -61,6 +63,11 @@
     }
 
     public synchronized String transact(String cmd) {
+        if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
+            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
+                    + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
+        }
+
         if (!connect()) {
             Slog.e(TAG, "connection failed");
             return "-1";
@@ -96,44 +103,50 @@
         }
     }
 
-    public int execute(String cmd) {
-        if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
-            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
-                    + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
-        }
-
-        String res = transact(cmd);
+    public void execute(String cmd, Object... args) throws InstallerException {
+        final String resRaw = executeForResult(cmd, args);
+        int res = -1;
         try {
-            return Integer.parseInt(res);
-        } catch (NumberFormatException ex) {
-            return -1;
+            res = Integer.parseInt(resRaw);
+        } catch (NumberFormatException ignored) {
+        }
+        if (res != 0) {
+            throw new InstallerException(
+                    "Failed to execute " + cmd + " " + Arrays.toString(args) + ": " + res);
         }
     }
 
-    public int dexopt(String apkPath, int uid, String instructionSet,
-            int dexoptNeeded, int dexFlags) {
-        return dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded,
-                null /*outputPath*/, dexFlags);
+    public String executeForResult(String cmd, Object... args)
+            throws InstallerException {
+        final StringBuilder builder = new StringBuilder(cmd);
+        for (Object arg : args) {
+            String escaped;
+            if (arg == null) {
+                escaped = "";
+            } else {
+                escaped = String.valueOf(arg);
+            }
+            if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) {
+                throw new InstallerException(
+                        "Invalid argument while executing " + cmd + " " + Arrays.toString(args));
+            }
+            if (TextUtils.isEmpty(escaped)) {
+                escaped = "!";
+            }
+            builder.append(' ').append(escaped);
+        }
+        return transact(builder.toString());
     }
 
-    public int dexopt(String apkPath, int uid, String pkgName, String instructionSet,
-            int dexoptNeeded, String outputPath, int dexFlags) {
-        StringBuilder builder = new StringBuilder("dexopt");
-        builder.append(' ');
-        builder.append(apkPath);
-        builder.append(' ');
-        builder.append(uid);
-        builder.append(' ');
-        builder.append(pkgName);
-        builder.append(' ');
-        builder.append(instructionSet);
-        builder.append(' ');
-        builder.append(dexoptNeeded);
-        builder.append(' ');
-        builder.append(outputPath != null ? outputPath : "!");
-        builder.append(' ');
-        builder.append(dexFlags);
-        return execute(builder.toString());
+    public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
+            int dexFlags) throws InstallerException {
+        dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded, null /* outputPath */, dexFlags);
+    }
+
+    public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+            int dexoptNeeded, String outputPath, int dexFlags) throws InstallerException {
+        execute("dexopt", apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
+                dexFlags);
     }
 
     private boolean connect() {
@@ -227,11 +240,19 @@
 
     public void waitForConnection() {
         for (;;) {
-            if (execute("ping") >= 0) {
+            try {
+                execute("ping");
                 return;
+            } catch (InstallerException ignored) {
             }
             Slog.w(TAG, "installd not ready");
             SystemClock.sleep(1000);
         }
     }
+
+    public static class InstallerException extends Exception {
+        public InstallerException(String detailMessage) {
+            super(detailMessage);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 4a1f7f4..eecc0ee 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -37,6 +37,8 @@
 import android.util.Log;
 import android.webkit.WebViewFactory;
 
+import com.android.internal.os.InstallerConnection.InstallerException;
+
 import dalvik.system.DexFile;
 import dalvik.system.PathClassLoader;
 import dalvik.system.VMRuntime;
@@ -502,8 +504,8 @@
                             dexoptNeeded, 0 /*dexFlags*/);
                 }
             }
-        } catch (IOException ioe) {
-            throw new RuntimeException("Error starting system_server", ioe);
+        } catch (IOException | InstallerException e) {
+            throw new RuntimeException("Error starting system_server", e);
         } finally {
             installer.disconnect();
         }
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index f159a4d..b4c4ef5 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2677,20 +2677,16 @@
                     invalidatePanelMenu(FEATURE_ACTION_BAR);
                 }
             } else {
-                mTitleView = (TextView)findViewById(R.id.title);
+                mTitleView = (TextView) findViewById(R.id.title);
                 if (mTitleView != null) {
-                    mTitleView.setLayoutDirection(mDecor.getLayoutDirection());
                     if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
-                        View titleContainer = findViewById(
-                                R.id.title_container);
+                        final View titleContainer = findViewById(R.id.title_container);
                         if (titleContainer != null) {
                             titleContainer.setVisibility(View.GONE);
                         } else {
                             mTitleView.setVisibility(View.GONE);
                         }
-                        if (mContentParent instanceof FrameLayout) {
-                            ((FrameLayout)mContentParent).setForeground(null);
-                        }
+                        mContentParent.setForeground(null);
                     } else {
                         mTitleView.setText(mTitle);
                     }
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 16bf9dd..ca1334c 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -26,6 +26,7 @@
 
 import java.lang.reflect.Array;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Objects;
 
 /**
@@ -372,6 +373,10 @@
         return (array != null) ? array.clone() : null;
     }
 
+    public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
+        return (array != null) ? new ArraySet<T>(array) : null;
+    }
+
     public static @NonNull <T> ArraySet<T> add(@Nullable ArraySet<T> cur, T val) {
         if (cur == null) {
             cur = new ArraySet<>();
@@ -420,6 +425,16 @@
         return (cur != null) ? cur.contains(val) : false;
     }
 
+    public static @Nullable <T> T[] trimToSize(@Nullable T[] array, int size) {
+        if (array == null || size == 0) {
+            return null;
+        } else if (array.length == size) {
+            return array;
+        } else {
+            return Arrays.copyOf(array, size);
+        }
+    }
+
     /**
      * Returns true if the two ArrayLists are equal with respect to the objects they contain.
      * The objects must be in the same order and be reference equal (== not .equals()).
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 799ed83..fda0ffa 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -60,19 +60,7 @@
     // as all the data needed is contained within the newly created LocalMatrixShader.
     SkASSERT(shaderHandle);
     SkAutoTUnref<SkShader> currentShader(reinterpret_cast<SkShader*>(shaderHandle));
-
-    SkMatrix currentMatrix;
-    SkAutoTUnref<SkShader> baseShader(currentShader->refAsALocalMatrixShader(&currentMatrix));
-    if (baseShader.get()) {
-        // if the matrices are same then there is no need to allocate a new
-        // shader that is identical to the existing one.
-        if (currentMatrix == *matrix) {
-            return reinterpret_cast<jlong>(currentShader.detach());
-        }
-        return reinterpret_cast<jlong>(SkShader::CreateLocalMatrixShader(baseShader, *matrix));
-    }
-
-    return reinterpret_cast<jlong>(SkShader::CreateLocalMatrixShader(currentShader, *matrix));
+    return reinterpret_cast<jlong>(currentShader->newWithLocalMatrix(*matrix));
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index e162810..8e8f6c3 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -152,7 +152,8 @@
 
 static struct {
     jmethodID postDynPolicyEventFromNative;
-} gDynPolicyEventHandlerMethods;
+    jmethodID postRecordConfigEventFromNative;
+} gAudioPolicyEventHandlerMethods;
 
 static Mutex gLock;
 
@@ -378,12 +379,26 @@
     const char* zechars = regId.string();
     jstring zestring = env->NewStringUTF(zechars);
 
-    env->CallStaticVoidMethod(clazz, gDynPolicyEventHandlerMethods.postDynPolicyEventFromNative,
+    env->CallStaticVoidMethod(clazz, gAudioPolicyEventHandlerMethods.postDynPolicyEventFromNative,
             event, zestring, val);
 
     env->ReleaseStringUTFChars(zestring, zechars);
     env->DeleteLocalRef(clazz);
+}
 
+static void
+android_media_AudioSystem_recording_callback(int event, int session, int source)
+{
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    if (env == NULL) {
+        return;
+    }
+
+    jclass clazz = env->FindClass(kClassPathName);
+    env->CallStaticVoidMethod(clazz,
+            gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
+            event, session, source);
+    env->DeleteLocalRef(clazz);
 }
 
 static jint
@@ -1503,6 +1518,12 @@
     AudioSystem::setDynPolicyCallback(android_media_AudioSystem_dyn_policy_callback);
 }
 
+static void
+android_media_AudioSystem_registerRecordingCallback(JNIEnv *env, jobject thiz)
+{
+    AudioSystem::setRecordConfigCallback(android_media_AudioSystem_recording_callback);
+}
+
 
 static jint convertAudioMixToNative(JNIEnv *env,
                                     AudioMix *nAudioMix,
@@ -1677,6 +1698,8 @@
                                             (void *)android_media_AudioSystem_registerPolicyMixes},
     {"native_register_dynamic_policy_callback", "()V",
                                     (void *)android_media_AudioSystem_registerDynPolicyCallback},
+    {"native_register_recording_callback", "()V",
+                                    (void *)android_media_AudioSystem_registerRecordingCallback},
     {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
 };
 
@@ -1780,9 +1803,12 @@
     gEventHandlerFields.mJniCallback = GetFieldIDOrDie(env,
                                                     eventHandlerClass, "mJniCallback", "J");
 
-    gDynPolicyEventHandlerMethods.postDynPolicyEventFromNative =
+    gAudioPolicyEventHandlerMethods.postDynPolicyEventFromNative =
             GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
                     "dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V");
+    gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative =
+            GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
+                    "recordingCallbackFromNative", "(III)V");
 
     jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
     gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 2488111..03a1e71 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -84,6 +84,7 @@
     jfieldID privateClean_field;
     jfieldID sharedClean_field;
     jfieldID swappedOut_field;
+    jfieldID swappedOutPss_field;
 };
 
 struct stat_field_names {
@@ -94,20 +95,22 @@
     const char* privateClean_name;
     const char* sharedClean_name;
     const char* swappedOut_name;
+    const char* swappedOutPss_name;
 };
 
 static stat_fields stat_fields[_NUM_CORE_HEAP];
 
 static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
     { "otherPss", "otherSwappablePss", "otherPrivateDirty", "otherSharedDirty",
-        "otherPrivateClean", "otherSharedClean", "otherSwappedOut" },
+        "otherPrivateClean", "otherSharedClean", "otherSwappedOut", "otherSwappedOutPss" },
     { "dalvikPss", "dalvikSwappablePss", "dalvikPrivateDirty", "dalvikSharedDirty",
-        "dalvikPrivateClean", "dalvikSharedClean", "dalvikSwappedOut" },
+        "dalvikPrivateClean", "dalvikSharedClean", "dalvikSwappedOut", "dalvikSwappedOutPss" },
     { "nativePss", "nativeSwappablePss", "nativePrivateDirty", "nativeSharedDirty",
-        "nativePrivateClean", "nativeSharedClean", "nativeSwappedOut" }
+        "nativePrivateClean", "nativeSharedClean", "nativeSwappedOut", "nativeSwappedOutPss" }
 };
 
 jfieldID otherStats_field;
+jfieldID hasSwappedOutPss_field;
 
 static bool memtrackLoaded;
 
@@ -119,6 +122,7 @@
     int privateClean;
     int sharedClean;
     int swappedOut;
+    int swappedOutPss;
 };
 
 #define BINDER_STATS "/proc/binder/stats"
@@ -206,7 +210,7 @@
     return err;
 }
 
-static void read_mapinfo(FILE *fp, stats_t* stats)
+static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss)
 {
     char line[1024];
     int len, nameLen;
@@ -216,7 +220,7 @@
     float sharing_proportion = 0.0;
     unsigned shared_clean = 0, shared_dirty = 0;
     unsigned private_clean = 0, private_dirty = 0;
-    unsigned swapped_out = 0;
+    unsigned swapped_out = 0, swapped_out_pss = 0;
     bool is_swappable = false;
     unsigned temp;
 
@@ -230,6 +234,8 @@
     int subHeap = HEAP_UNKNOWN;
     int prevHeap = HEAP_UNKNOWN;
 
+    *foundSwapPss = false;
+
     if(fgets(line, sizeof(line), fp) == 0) return;
 
     while (!done) {
@@ -340,6 +346,7 @@
         private_clean = 0;
         private_dirty = 0;
         swapped_out = 0;
+        swapped_out_pss = 0;
 
         while (true) {
             if (fgets(line, 1024, fp) == 0) {
@@ -365,6 +372,9 @@
                 /* referenced = temp; */
             } else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) {
                 swapped_out = temp;
+            } else if (line[0] == 'S' && sscanf(line, "SwapPss: %d kB", &temp) == 1) {
+                *foundSwapPss = true;
+                swapped_out_pss = temp;
             } else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) {
                 // looks like a new mapping
                 // example: "10000000-10001000 ---p 10000000 00:00 0"
@@ -390,6 +400,7 @@
             stats[whichHeap].privateClean += private_clean;
             stats[whichHeap].sharedClean += shared_clean;
             stats[whichHeap].swappedOut += swapped_out;
+            stats[whichHeap].swappedOutPss += swapped_out_pss;
             if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER) {
                 stats[subHeap].pss += pss;
                 stats[subHeap].swappablePss += swappable_pss;
@@ -398,12 +409,13 @@
                 stats[subHeap].privateClean += private_clean;
                 stats[subHeap].sharedClean += shared_clean;
                 stats[subHeap].swappedOut += swapped_out;
+                stats[subHeap].swappedOutPss += swapped_out_pss;
             }
         }
     }
 }
 
-static void load_maps(int pid, stats_t* stats)
+static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
 {
     char tmp[128];
     FILE *fp;
@@ -412,17 +424,18 @@
     fp = fopen(tmp, "r");
     if (fp == 0) return;
 
-    read_mapinfo(fp, stats);
+    read_mapinfo(fp, stats, foundSwapPss);
     fclose(fp);
 }
 
 static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
         jint pid, jobject object)
 {
+    bool foundSwapPss;
     stats_t stats[_NUM_HEAP];
     memset(&stats, 0, sizeof(stats));
 
-    load_maps(pid, stats);
+    load_maps(pid, stats, &foundSwapPss);
 
     struct graphics_memory_pss graphics_mem;
     if (read_memtrack_memory(pid, &graphics_mem) == 0) {
@@ -442,6 +455,7 @@
         stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean;
         stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean;
         stats[HEAP_UNKNOWN].swappedOut += stats[i].swappedOut;
+        stats[HEAP_UNKNOWN].swappedOutPss += stats[i].swappedOutPss;
     }
 
     for (int i=0; i<_NUM_CORE_HEAP; i++) {
@@ -452,9 +466,11 @@
         env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean);
         env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean);
         env->SetIntField(object, stat_fields[i].swappedOut_field, stats[i].swappedOut);
+        env->SetIntField(object, stat_fields[i].swappedOutPss_field, stats[i].swappedOutPss);
     }
 
 
+    env->SetBooleanField(object, hasSwappedOutPss_field, foundSwapPss);
     jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
 
     jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
@@ -471,6 +487,7 @@
         otherArray[j++] = stats[i].privateClean;
         otherArray[j++] = stats[i].sharedClean;
         otherArray[j++] = stats[i].swappedOut;
+        otherArray[j++] = stats[i].swappedOutPss;
     }
 
     env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
@@ -481,11 +498,12 @@
     android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
 }
 
-static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid, jlongArray outUss,
-        jlongArray outMemtrack)
+static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid,
+        jlongArray outUssSwapPss, jlongArray outMemtrack)
 {
     char line[1024];
     jlong pss = 0;
+    jlong swapPss = 0;
     jlong uss = 0;
     jlong memtrack = 0;
 
@@ -521,19 +539,31 @@
                     }
                     uss += atoi(c);
                 }
+            } else if (line[0] == 'S' && strncmp(line, "SwapPss:", 8) == 0) {
+                char* c = line + 8;
+                jlong lSwapPss;
+                while (*c != 0 && (*c < '0' || *c > '9')) {
+                    c++;
+                }
+                lSwapPss = atoi(c);
+                swapPss += lSwapPss;
+                pss += lSwapPss; // Also in swap, those pages would be accounted as Pss without SWAP
             }
         }
 
         fclose(fp);
     }
 
-    if (outUss != NULL) {
-        if (env->GetArrayLength(outUss) >= 1) {
-            jlong* outUssArray = env->GetLongArrayElements(outUss, 0);
-            if (outUssArray != NULL) {
-                outUssArray[0] = uss;
+    if (outUssSwapPss != NULL) {
+        if (env->GetArrayLength(outUssSwapPss) >= 1) {
+            jlong* outUssSwapPssArray = env->GetLongArrayElements(outUssSwapPss, 0);
+            if (outUssSwapPssArray != NULL) {
+                outUssSwapPssArray[0] = uss;
+                if (env->GetArrayLength(outUssSwapPss) >= 2) {
+                    outUssSwapPssArray[1] = swapPss;
+                }
             }
-            env->ReleaseLongArrayElements(outUss, outUssArray, 0);
+            env->ReleaseLongArrayElements(outUssSwapPss, outUssSwapPssArray, 0);
         }
     }
 
@@ -1056,6 +1086,7 @@
     }
 
     otherStats_field = env->GetFieldID(clazz, "otherStats", "[I");
+    hasSwappedOutPss_field = env->GetFieldID(clazz, "hasSwappedOutPss", "Z");
 
     for (int i=0; i<_NUM_CORE_HEAP; i++) {
         stat_fields[i].pss_field =
@@ -1072,6 +1103,8 @@
                 env->GetFieldID(clazz, stat_field_names[i].sharedClean_name, "I");
         stat_fields[i].swappedOut_field =
                 env->GetFieldID(clazz, stat_field_names[i].swappedOut_name, "I");
+        stat_fields[i].swappedOutPss_field =
+                env->GetFieldID(clazz, stat_field_names[i].swappedOutPss_name, "I");
     }
 
     return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 651b453..e2150c0 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -82,8 +82,13 @@
      */
     public static final int PADDING_MODE_STACK = 1;
 
-    /** Value used for undefined start and end insets. */
-    private static final int UNDEFINED_INSET = Integer.MIN_VALUE;
+    /**
+     * Value used for undefined start and end insets.
+     *
+     * @see #getLayerInsetStart(int)
+     * @see #getLayerInsetEnd(int)
+     */
+    public static final int UNDEFINED_INSET = Integer.MIN_VALUE;
 
     LayerState mLayerState;
 
@@ -867,7 +872,8 @@
 
     /**
      * @param index the index of the layer
-     * @return number of pixels to inset from the start bound
+     * @return the number of pixels to inset from the start bound, or
+     *         {@link #UNDEFINED_INSET} if not specified
      * @attr ref android.R.styleable#LayerDrawableItem_start
      */
     public int getLayerInsetStart(int index) {
@@ -877,7 +883,8 @@
 
     /**
      * @param index the index of the layer to adjust
-     * @param e number of pixels to inset from the end bound
+     * @param e number of pixels to inset from the end bound, or
+     *         {@link #UNDEFINED_INSET} if not specified
      * @attr ref android.R.styleable#LayerDrawableItem_end
      */
     public void setLayerInsetEnd(int index, int e) {
@@ -977,34 +984,33 @@
             computeStackedPadding(padding);
         }
 
+        final int paddingT = layerState.mPaddingTop;
+        final int paddingB = layerState.mPaddingBottom;
+
+        // Resolve padding for RTL. Relative padding overrides absolute
+        // padding.
+        final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL;
+        final int paddingRtlL = isLayoutRtl ? layerState.mPaddingEnd : layerState.mPaddingStart;
+        final int paddingRtlR = isLayoutRtl ? layerState.mPaddingStart : layerState.mPaddingEnd;
+        final int paddingL = paddingRtlL >= 0 ? paddingRtlL : layerState.mPaddingLeft;
+        final int paddingR = paddingRtlR >= 0 ? paddingRtlR : layerState.mPaddingRight;
+
         // If padding was explicitly specified (e.g. not -1) then override the
         // computed padding in that dimension.
-        if (layerState.mPaddingTop >= 0) {
-            padding.top = layerState.mPaddingTop;
+        if (paddingL >= 0) {
+            padding.left = paddingL;
         }
 
-        if (layerState.mPaddingBottom >= 0) {
-            padding.bottom = layerState.mPaddingBottom;
+        if (paddingT >= 0) {
+            padding.top = paddingT;
         }
 
-        final int paddingRtlLeft;
-        final int paddingRtlRight;
-        if (getLayoutDirection() == LayoutDirection.RTL) {
-            paddingRtlLeft = layerState.mPaddingEnd;
-            paddingRtlRight = layerState.mPaddingStart;
-        } else {
-            paddingRtlLeft = layerState.mPaddingStart;
-            paddingRtlRight = layerState.mPaddingEnd;
+        if (paddingR >= 0) {
+            padding.right = paddingR;
         }
 
-        final int paddingLeft =  paddingRtlLeft >= 0 ? paddingRtlLeft : layerState.mPaddingLeft;
-        if (paddingLeft >= 0) {
-            padding.left = paddingLeft;
-        }
-
-        final int paddingRight =  paddingRtlRight >= 0 ? paddingRtlRight : layerState.mPaddingRight;
-        if (paddingRight >= 0) {
-            padding.right = paddingRight;
+        if (paddingB >= 0) {
+            padding.bottom = paddingB;
         }
 
         return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0;
@@ -1471,58 +1477,59 @@
     }
 
     private void updateLayerBounds(Rect bounds) {
-        int padL = 0;
-        int padT = 0;
-        int padR = 0;
-        int padB = 0;
+        int paddingL = 0;
+        int paddingT = 0;
+        int paddingR = 0;
+        int paddingB = 0;
 
         final Rect outRect = mTmpOutRect;
         final int layoutDirection = getLayoutDirection();
-        final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
+        final boolean isLayoutRtl = layoutDirection == LayoutDirection.RTL;
+        final boolean isPaddingNested = mLayerState.mPaddingMode == PADDING_MODE_NEST;
         final ChildDrawable[] array = mLayerState.mChildren;
-        final int N = mLayerState.mNum;
-        for (int i = 0; i < N; i++) {
+
+        for (int i = 0, count = mLayerState.mNum; i < count; i++) {
             final ChildDrawable r = array[i];
             final Drawable d = r.mDrawable;
             if (d == null) {
                 continue;
             }
 
-            final Rect container = mTmpContainer;
-            container.set(d.getBounds());
+            final int insetT = r.mInsetT;
+            final int insetB = r.mInsetB;
 
-            // Take the resolved layout direction into account. If start / end
-            // padding are defined, they will be resolved (hence overriding) to
-            // left / right or right / left depending on the resolved layout
-            // direction. If start / end padding are not defined, use the
-            // left / right ones.
-            final int insetL, insetR;
-            if (layoutDirection == LayoutDirection.RTL) {
-                insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE;
-                insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS;
-            } else {
-                insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS;
-                insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
-            }
+            // Resolve insets for RTL. Relative insets override absolute
+            // insets.
+            final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS;
+            final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE;
+            final int insetL = insetRtlL == UNDEFINED_INSET ? r.mInsetL : insetRtlL;
+            final int insetR = insetRtlR == UNDEFINED_INSET ? r.mInsetR : insetRtlR;
 
             // Establish containing region based on aggregate padding and
             // requested insets for the current layer.
-            container.set(bounds.left + insetL + padL, bounds.top + r.mInsetT + padT,
-                    bounds.right - insetR - padR, bounds.bottom - r.mInsetB - padB);
+            final Rect container = mTmpContainer;
+            container.set(bounds.left + insetL + paddingL, bounds.top + insetT + paddingT,
+                    bounds.right - insetR - paddingR, bounds.bottom - insetB - paddingB);
 
-            // Apply resolved gravity to drawable based on resolved size.
-            final int gravity = resolveGravity(r.mGravity, r.mWidth, r.mHeight,
-                    d.getIntrinsicWidth(), d.getIntrinsicHeight());
-            final int w = r.mWidth < 0 ? d.getIntrinsicWidth() : r.mWidth;
-            final int h = r.mHeight < 0 ? d.getIntrinsicHeight() : r.mHeight;
-            Gravity.apply(gravity, w, h, container, outRect, layoutDirection);
+            // Compute a reasonable default gravity based on the intrinsic and
+            // explicit dimensions, if specified.
+            final int intrinsicW = d.getIntrinsicWidth();
+            final int intrinsicH = d.getIntrinsicHeight();
+            final int layerW = r.mWidth;
+            final int layerH = r.mHeight;
+            final int gravity = resolveGravity(r.mGravity, layerW, layerH, intrinsicW, intrinsicH);
+
+            // Explicit dimensions override intrinsic dimensions.
+            final int resolvedW = layerW < 0 ? intrinsicW : layerW;
+            final int resolvedH = layerH < 0 ? intrinsicH : layerH;
+            Gravity.apply(gravity, resolvedW, resolvedH, container, outRect, layoutDirection);
             d.setBounds(outRect);
 
-            if (nest) {
-                padL += mPaddingL[i];
-                padR += mPaddingR[i];
-                padT += mPaddingT[i];
-                padB += mPaddingB[i];
+            if (isPaddingNested) {
+                paddingL += mPaddingL[i];
+                paddingR += mPaddingR[i];
+                paddingT += mPaddingT[i];
+                paddingB += mPaddingB[i];
             }
         }
     }
@@ -1578,6 +1585,7 @@
         int padR = 0;
 
         final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
+        final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL;
         final ChildDrawable[] array = mLayerState.mChildren;
         final int N = mLayerState.mNum;
         for (int i = 0; i < N; i++) {
@@ -1591,15 +1599,10 @@
             // left / right or right / left depending on the resolved layout
             // direction. If start / end padding are not defined, use the
             // left / right ones.
-            final int insetL, insetR;
-            final int layoutDirection = getLayoutDirection();
-            if (layoutDirection == LayoutDirection.RTL) {
-                insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE;
-                insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS;
-            } else {
-                insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS;
-                insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
-            }
+            final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS;
+            final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE;
+            final int insetL = insetRtlL == UNDEFINED_INSET ? r.mInsetL : insetRtlL;
+            final int insetR = insetRtlR == UNDEFINED_INSET ? r.mInsetR : insetRtlR;
 
             // Don't apply padding and insets for children that don't have
             // an intrinsic dimension.
@@ -1659,8 +1662,8 @@
         if (r.mDrawable != null) {
             final Rect rect = mTmpRect;
             r.mDrawable.getPadding(rect);
-            if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i] ||
-                    rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) {
+            if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i]
+                    || rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) {
                 mPaddingL[i] = rect.left;
                 mPaddingT[i] = rect.top;
                 mPaddingR[i] = rect.right;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 0932e89..44c5e2f 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -109,8 +109,8 @@
         BakedOpDispatcher.cpp \
         BakedOpRenderer.cpp \
         BakedOpState.cpp \
-        FrameReorderer.cpp \
-        LayerReorderer.cpp \
+        FrameBuilder.cpp \
+        LayerBuilder.cpp \
         RecordingCanvas.cpp
 
     hwui_cflags += -DHWUI_NEW_OPS
@@ -237,7 +237,7 @@
 ifeq (true, $(HWUI_NEW_OPS))
     LOCAL_SRC_FILES += \
         tests/unit/BakedOpStateTests.cpp \
-        tests/unit/FrameReordererTests.cpp \
+        tests/unit/FrameBuilderTests.cpp \
         tests/unit/RecordingCanvasTests.cpp
 endif
 
@@ -299,7 +299,7 @@
 
 ifeq (true, $(HWUI_NEW_OPS))
     LOCAL_SRC_FILES += \
-        tests/microbench/FrameReordererBench.cpp
+        tests/microbench/FrameBuilderBench.cpp
 endif
 
 include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 23aca89..5b34f6b 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -545,19 +545,19 @@
             op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
 
     Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
-    if (!texture) return;
-    const AutoTexture autoCleanup(texture);
-    Glop glop;
-    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
-            .setRoundRectClipState(state.roundRectClipState)
-            .setMeshPatchQuads(*mesh)
-            .setMeshTexturedUnitQuad(texture->uvMapper)
-            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
-            .setTransform(state.computedState.transform, TransformFlags::None)
-            .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
-                    Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
-            .build();
-    renderer.renderGlop(state, glop);
+    if (CC_LIKELY(texture)) {
+        const AutoTexture autoCleanup(texture);
+        Glop glop;
+        GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+                .setRoundRectClipState(state.roundRectClipState)
+                .setMeshPatchQuads(*mesh)
+                .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
+                .setTransform(state.computedState.transform, TransformFlags::None)
+                .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
+                        Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
+                .build();
+        renderer.renderGlop(state, glop);
+    }
 }
 
 void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index c1f19a3..42fb66f 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -161,7 +161,7 @@
 }
 
 void BakedOpRenderer::clearColorBuffer(const Rect& rect) {
-    if (Rect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight).contains(rect)) {
+    if (rect.contains(Rect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight))) {
         // Full viewport is being cleared - disable scissor
         mRenderState.scissor().setEnabled(false);
     } else {
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
index f0406fa..87844f9 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -71,7 +71,6 @@
     clipState = snapshot.mutateClipArea().serializeClip(allocator);
     LOG_ALWAYS_FATAL_IF(!clipState, "clipState required");
     clippedBounds = clipState->rect;
-    transform.mapRect(clippedBounds);
     clipSideFlags = OpClipSideFlags::Full;
 }
 
diff --git a/libs/hwui/FrameReorderer.cpp b/libs/hwui/FrameBuilder.cpp
similarity index 88%
rename from libs/hwui/FrameReorderer.cpp
rename to libs/hwui/FrameBuilder.cpp
index 4bfc0b4..166656c 100644
--- a/libs/hwui/FrameReorderer.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "FrameReorderer.h"
+#include "FrameBuilder.h"
 
 #include "LayerUpdateQueue.h"
 #include "RenderNode.h"
@@ -30,24 +30,25 @@
 namespace android {
 namespace uirenderer {
 
-FrameReorderer::FrameReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
         uint32_t viewportWidth, uint32_t viewportHeight,
         const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter)
         : mCanvasState(*this) {
     ATRACE_NAME("prepare drawing commands");
 
-    mLayerReorderers.reserve(layers.entries().size());
+    mLayerBuilders.reserve(layers.entries().size());
     mLayerStack.reserve(layers.entries().size());
 
     // Prepare to defer Fbo0
-    mLayerReorderers.emplace_back(viewportWidth, viewportHeight, Rect(clip));
+    auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip));
+    mLayerBuilders.push_back(fbo0);
     mLayerStack.push_back(0);
     mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
             clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
             lightCenter);
 
     // Render all layers to be updated, in order. Defer in reverse order, so that they'll be
-    // updated in the order they're passed in (mLayerReorderers are issued to Renderer in reverse)
+    // updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse)
     for (int i = layers.entries().size() - 1; i >= 0; i--) {
         RenderNode* layerNode = layers.entries()[i].renderNode;
         const Rect& layerDamage = layers.entries()[i].damage;
@@ -77,11 +78,11 @@
     }
 }
 
-void FrameReorderer::onViewportInitialized() {}
+void FrameBuilder::onViewportInitialized() {}
 
-void FrameReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+void FrameBuilder::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
 
-void FrameReorderer::deferNodePropsAndOps(RenderNode& node) {
+void FrameBuilder::deferNodePropsAndOps(RenderNode& node) {
     const RenderProperties& properties = node.properties();
     const Outline& outline = properties.getOutline();
     if (properties.getAlpha() <= 0
@@ -213,7 +214,7 @@
 }
 
 template <typename V>
-void FrameReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
+void FrameBuilder::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
     const int size = zTranslatedNodes.size();
     if (size == 0
             || (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f)
@@ -263,7 +264,7 @@
     }
 }
 
-void FrameReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
+void FrameBuilder::deferShadow(const RenderNodeOp& casterNodeOp) {
     auto& node = *casterNodeOp.renderNode;
     auto& properties = node.properties();
 
@@ -319,7 +320,7 @@
     }
 }
 
-void FrameReorderer::deferProjectedChildren(const RenderNode& renderNode) {
+void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) {
     const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();
     int count = mCanvasState.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
 
@@ -352,15 +353,15 @@
 }
 
 /**
- * Used to define a list of lambdas referencing private FrameReorderer::onXX::defer() methods.
+ * Used to define a list of lambdas referencing private FrameBuilder::onXX::defer() methods.
  *
  * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas.
- * E.g. a BitmapOp op then would be dispatched to FrameReorderer::onBitmapOp(const BitmapOp&)
+ * E.g. a BitmapOp op then would be dispatched to FrameBuilder::onBitmapOp(const BitmapOp&)
  */
 #define OP_RECEIVER(Type) \
-        [](FrameReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); },
-void FrameReorderer::deferNodeOps(const RenderNode& renderNode) {
-    typedef void (*OpDispatcher) (FrameReorderer& reorderer, const RecordedOp& op);
+        [](FrameBuilder& frameBuilder, const RecordedOp& op) { frameBuilder.defer##Type(static_cast<const Type&>(op)); },
+void FrameBuilder::deferNodeOps(const RenderNode& renderNode) {
+    typedef void (*OpDispatcher) (FrameBuilder& frameBuilder, const RecordedOp& op);
     static OpDispatcher receivers[] = BUILD_DEFERRABLE_OP_LUT(OP_RECEIVER);
 
     // can't be null, since DL=null node rejection happens before deferNodePropsAndOps
@@ -384,7 +385,7 @@
     }
 }
 
-void FrameReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
+void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) {
     if (op.renderNode->nothingToDraw()) return;
     int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
 
@@ -399,7 +400,7 @@
     mCanvasState.restoreToCount(count);
 }
 
-void FrameReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
+void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) {
     if (!op.skipInOrderDraw) {
         deferRenderNodeOpImpl(op);
     }
@@ -409,7 +410,7 @@
  * Defers an unmergeable, strokeable op, accounting correctly
  * for paint's style on the bounds being computed.
  */
-void FrameReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+void FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
         BakedOpState::StrokeBehavior strokeBehavior) {
     // Note: here we account for stroke when baking the op
     BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
@@ -431,7 +432,7 @@
             : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
 }
 
-void FrameReorderer::deferArcOp(const ArcOp& op) {
+void FrameBuilder::deferArcOp(const ArcOp& op) {
     deferStrokeableOp(op, tessBatchId(op));
 }
 
@@ -440,7 +441,7 @@
             || state.computedState.clipState->mode == ClipMode::Rectangle;
 }
 
-void FrameReorderer::deferBitmapOp(const BitmapOp& op) {
+void FrameBuilder::deferBitmapOp(const BitmapOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
 
@@ -452,7 +453,7 @@
             && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
             && op.bitmap->colorType() != kAlpha_8_SkColorType
             && hasMergeableClip(*bakedState)) {
-        mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
+        mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
         // TODO: AssetAtlas in mergeId
         currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId);
     } else {
@@ -460,19 +461,19 @@
     }
 }
 
-void FrameReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) {
+void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
 }
 
-void FrameReorderer::deferBitmapRectOp(const BitmapRectOp& op) {
+void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
 }
 
-void FrameReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
+void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) {
     // allocate a temporary oval op (with mAllocator, so it persists until render), so the
     // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
     float x = *(op.x);
@@ -487,29 +488,29 @@
     deferOvalOp(*resolvedOp);
 }
 
-void FrameReorderer::deferFunctorOp(const FunctorOp& op) {
+void FrameBuilder::deferFunctorOp(const FunctorOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor);
 }
 
-void FrameReorderer::deferLinesOp(const LinesOp& op) {
+void FrameBuilder::deferLinesOp(const LinesOp& op) {
     batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
     deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
 }
 
-void FrameReorderer::deferOvalOp(const OvalOp& op) {
+void FrameBuilder::deferOvalOp(const OvalOp& op) {
     deferStrokeableOp(op, tessBatchId(op));
 }
 
-void FrameReorderer::deferPatchOp(const PatchOp& op) {
+void FrameBuilder::deferPatchOp(const PatchOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
 
     if (bakedState->computedState.transform.isPureTranslate()
             && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
             && hasMergeableClip(*bakedState)) {
-        mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
+        mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
         // TODO: AssetAtlas in mergeId
 
         // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together
@@ -520,24 +521,24 @@
     }
 }
 
-void FrameReorderer::deferPathOp(const PathOp& op) {
+void FrameBuilder::deferPathOp(const PathOp& op) {
     deferStrokeableOp(op, OpBatchType::Bitmap);
 }
 
-void FrameReorderer::deferPointsOp(const PointsOp& op) {
+void FrameBuilder::deferPointsOp(const PointsOp& op) {
     batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
     deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
 }
 
-void FrameReorderer::deferRectOp(const RectOp& op) {
+void FrameBuilder::deferRectOp(const RectOp& op) {
     deferStrokeableOp(op, tessBatchId(op));
 }
 
-void FrameReorderer::deferRoundRectOp(const RoundRectOp& op) {
+void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) {
     deferStrokeableOp(op, tessBatchId(op));
 }
 
-void FrameReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
+void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
     // allocate a temporary round rect op (with mAllocator, so it persists until render), so the
     // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
     const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp(
@@ -548,7 +549,7 @@
     deferRoundRectOp(*resolvedOp);
 }
 
-void FrameReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) {
+void FrameBuilder::deferSimpleRectsOp(const SimpleRectsOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
@@ -559,7 +560,7 @@
     return paint.getColor() == SK_ColorBLACK ? OpBatchType::Text : OpBatchType::ColorText;
 }
 
-void FrameReorderer::deferTextOp(const TextOp& op) {
+void FrameBuilder::deferTextOp(const TextOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
 
@@ -574,19 +575,19 @@
     }
 }
 
-void FrameReorderer::deferTextOnPathOp(const TextOnPathOp& op) {
+void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint)));
 }
 
-void FrameReorderer::deferTextureLayerOp(const TextureLayerOp& op) {
+void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer);
 }
 
-void FrameReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+void FrameBuilder::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
         float contentTranslateX, float contentTranslateY,
         const Rect& repaintRect,
         const Vector3& lightCenter,
@@ -601,11 +602,13 @@
             repaintRect.left, repaintRect.top, repaintRect.right, repaintRect.bottom);
 
     // create a new layer repaint, and push its index on the stack
-    mLayerStack.push_back(mLayerReorderers.size());
-    mLayerReorderers.emplace_back(layerWidth, layerHeight, repaintRect, beginLayerOp, renderNode);
+    mLayerStack.push_back(mLayerBuilders.size());
+    auto newFbo = mAllocator.create<LayerBuilder>(layerWidth, layerHeight,
+            repaintRect, beginLayerOp, renderNode);
+    mLayerBuilders.push_back(newFbo);
 }
 
-void FrameReorderer::restoreForLayer() {
+void FrameBuilder::restoreForLayer() {
     // restore canvas, and pop finished layer off of the stack
     mCanvasState.restore();
     mLayerStack.pop_back();
@@ -613,7 +616,7 @@
 
 // TODO: defer time rejection (when bounds become empty) + tests
 // Option - just skip layers with no bounds at playback + defer?
-void FrameReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
+void FrameBuilder::deferBeginLayerOp(const BeginLayerOp& op) {
     uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
     uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
 
@@ -658,7 +661,7 @@
             &op, nullptr);
 }
 
-void FrameReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
+void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) {
     const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
     int finishedLayerIndex = mLayerStack.back();
 
@@ -671,7 +674,7 @@
             beginLayerOp.localMatrix,
             beginLayerOp.localClip,
             beginLayerOp.paint,
-            &mLayerReorderers[finishedLayerIndex].offscreenBuffer);
+            &(mLayerBuilders[finishedLayerIndex]->offscreenBuffer));
     BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
 
     if (bakedOpState) {
@@ -681,12 +684,12 @@
         // Layer won't be drawn - delete its drawing batches to prevent it from doing any work
         // TODO: need to prevent any render work from being done
         // - create layerop earlier for reject purposes?
-        mLayerReorderers[finishedLayerIndex].clear();
+        mLayerBuilders[finishedLayerIndex]->clear();
         return;
     }
 }
 
-void FrameReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
+void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
     Matrix4 boundsTransform(*(mCanvasState.currentSnapshot()->transform));
     boundsTransform.multiply(op.localMatrix);
 
@@ -721,7 +724,7 @@
     currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
 }
 
-void FrameReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
+void FrameBuilder::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
     LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");
 
     BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back();
diff --git a/libs/hwui/FrameReorderer.h b/libs/hwui/FrameBuilder.h
similarity index 89%
rename from libs/hwui/FrameReorderer.h
rename to libs/hwui/FrameBuilder.h
index 562e6a1..3ba73f0 100644
--- a/libs/hwui/FrameReorderer.h
+++ b/libs/hwui/FrameBuilder.h
@@ -19,7 +19,7 @@
 #include "BakedOpState.h"
 #include "CanvasState.h"
 #include "DisplayList.h"
-#include "LayerReorderer.h"
+#include "LayerBuilder.h"
 #include "RecordedOp.h"
 
 #include <vector>
@@ -42,7 +42,7 @@
  * Resolves final drawing state for each operation (including clip, alpha and matrix), and then
  * reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either
  * from the LayerUpdateQueue, or temporary layers created by saveLayer operations in the
- * draw stream) will create different reorder contexts, each in its own LayerReorderer.
+ * draw stream) will create different reorder contexts, each in its own LayerBuilder.
  *
  * Then the prepared or 'baked' drawing commands can be issued by calling the templated
  * replayBakedOps() function, which will dispatch them (including any created merged op collections)
@@ -52,13 +52,13 @@
  * This class is also the authoritative source for traversing RenderNodes, both for standard op
  * traversal within a DisplayList, and for out of order RenderNode traversal for Z and projection.
  */
-class FrameReorderer : public CanvasStateClient {
+class FrameBuilder : public CanvasStateClient {
 public:
-    FrameReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+    FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
             uint32_t viewportWidth, uint32_t viewportHeight,
             const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter);
 
-    virtual ~FrameReorderer() {}
+    virtual ~FrameBuilder() {}
 
     /**
      * replayBakedOps() is templated based on what class will receive ops being replayed.
@@ -98,8 +98,8 @@
 
         // Relay through layers in reverse order, since layers
         // later in the list will be drawn by earlier ones
-        for (int i = mLayerReorderers.size() - 1; i >= 1; i--) {
-            LayerReorderer& layer = mLayerReorderers[i];
+        for (int i = mLayerBuilders.size() - 1; i >= 1; i--) {
+            LayerBuilder& layer = *(mLayerBuilders[i]);
             if (layer.renderNode) {
                 // cached HW layer - can't skip layer if empty
                 renderer.startRepaintLayer(layer.offscreenBuffer, layer.repaintRect);
@@ -112,15 +112,15 @@
             }
         }
 
-        const LayerReorderer& fbo0 = mLayerReorderers[0];
+        const LayerBuilder& fbo0 = *(mLayerBuilders[0]);
         renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
         fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
         renderer.endFrame(fbo0.repaintRect);
     }
 
     void dump() const {
-        for (auto&& layer : mLayerReorderers) {
-            layer.dump();
+        for (auto&& layer : mLayerBuilders) {
+            layer->dump();
         }
     }
 
@@ -143,7 +143,7 @@
             const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
     void restoreForLayer();
 
-    LayerReorderer& currentLayer() { return mLayerReorderers[mLayerStack.back()]; }
+    LayerBuilder& currentLayer() { return *(mLayerBuilders[mLayerStack.back()]); }
 
     BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
         return BakedOpState::tryConstruct(mAllocator, *mCanvasState.writableSnapshot(), recordedOp);
@@ -173,7 +173,7 @@
             BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
 
     /**
-     * Declares all FrameReorderer::deferXXXXOp() methods for every RecordedOp type.
+     * Declares all FrameBuilder::deferXXXXOp() methods for every RecordedOp type.
      *
      * These private methods are called from within deferImpl to defer each individual op
      * type differently.
@@ -183,17 +183,17 @@
 #undef X
 
     // List of every deferred layer's render state. Replayed in reverse order to render a frame.
-    std::vector<LayerReorderer> mLayerReorderers;
+    std::vector<LayerBuilder*> mLayerBuilders;
 
     /*
-     * Stack of indices within mLayerReorderers representing currently active layers. If drawing
+     * Stack of indices within mLayerBuilders representing currently active layers. If drawing
      * layerA within a layerB, will contain, in order:
      *  - 0 (representing FBO 0, always present)
      *  - layerB's index
      *  - layerA's index
      *
-     * Note that this doesn't vector doesn't always map onto all values of mLayerReorderers. When a
-     * layer is finished deferring, it will still be represented in mLayerReorderers, but it's index
+     * Note that this doesn't vector doesn't always map onto all values of mLayerBuilders. When a
+     * layer is finished deferring, it will still be represented in mLayerBuilders, but it's index
      * won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
      * ops added to it.
     */
diff --git a/libs/hwui/LayerReorderer.cpp b/libs/hwui/LayerBuilder.cpp
similarity index 95%
rename from libs/hwui/LayerReorderer.cpp
rename to libs/hwui/LayerBuilder.cpp
index 9a17e93..7170d4f 100644
--- a/libs/hwui/LayerReorderer.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "LayerReorderer.h"
+#include "LayerBuilder.h"
 
 #include "BakedOpState.h"
 #include "RenderNode.h"
@@ -53,7 +53,7 @@
 
     void dump() const {
         ALOGD("    Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
-                this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds));
+                this, mBatchId, mMerging, (int) mOps.size(), RECT_ARGS(mBounds));
     }
 protected:
     batchid_t mBatchId;
@@ -202,7 +202,7 @@
     int mClipSideFlags;
 };
 
-LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
+LayerBuilder::LayerBuilder(uint32_t width, uint32_t height,
         const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
         : width(width)
         , height(height)
@@ -214,7 +214,7 @@
 
 // iterate back toward target to see if anything drawn since should overlap the new op
 // if no target, merging ops still iterate to find similar batch to insert after
-void LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
+void LayerBuilder::locateInsertIndex(int batchId, const Rect& clippedBounds,
         BatchBase** targetBatch, size_t* insertBatchIndex) const {
     for (int i = mBatches.size() - 1; i >= 0; i--) {
         BatchBase* overBatch = mBatches[i];
@@ -237,11 +237,11 @@
     }
 }
 
-void LayerReorderer::deferLayerClear(const Rect& rect) {
+void LayerBuilder::deferLayerClear(const Rect& rect) {
     mClearRects.push_back(rect);
 }
 
-void LayerReorderer::flushLayerClears(LinearAllocator& allocator) {
+void LayerBuilder::flushLayerClears(LinearAllocator& allocator) {
     if (CC_UNLIKELY(!mClearRects.empty())) {
         const int vertCount = mClearRects.size() * 4;
         // put the verts in the frame allocator, since
@@ -269,13 +269,11 @@
                 verts, vertCount);
         BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
                 &viewportClip, bounds, *op);
-
-
         deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
     }
 }
 
-void LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
+void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator,
         BakedOpState* op, batchid_t batchId) {
     if (batchId != OpBatchType::CopyToLayer) {
         // if first op after one or more unclipped saveLayers, flush the layer clears
@@ -300,7 +298,7 @@
     }
 }
 
-void LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
+void LayerBuilder::deferMergeableOp(LinearAllocator& allocator,
         BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
     if (batchId != OpBatchType::CopyToLayer) {
         // if first op after one or more unclipped saveLayers, flush the layer clears
@@ -332,7 +330,7 @@
     }
 }
 
-void LayerReorderer::replayBakedOpsImpl(void* arg,
+void LayerBuilder::replayBakedOpsImpl(void* arg,
         BakedOpReceiver* unmergedReceivers, MergedOpReceiver* mergedReceivers) const {
     ATRACE_NAME("flush drawing commands");
     for (const BatchBase* batch : mBatches) {
@@ -355,8 +353,8 @@
     }
 }
 
-void LayerReorderer::dump() const {
-    ALOGD("LayerReorderer %p, %ux%u buffer %p, blo %p, rn %p",
+void LayerBuilder::dump() const {
+    ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p",
             this, width, height, offscreenBuffer, beginLayerOp, renderNode);
     for (const BatchBase* batch : mBatches) {
         batch->dump();
diff --git a/libs/hwui/LayerReorderer.h b/libs/hwui/LayerBuilder.h
similarity index 87%
rename from libs/hwui/LayerReorderer.h
rename to libs/hwui/LayerBuilder.h
index 83cda81..99968e1 100644
--- a/libs/hwui/LayerReorderer.h
+++ b/libs/hwui/LayerBuilder.h
@@ -18,6 +18,7 @@
 
 #include "ClipArea.h"
 #include "Rect.h"
+#include "utils/Macros.h"
 
 #include <vector>
 #include <unordered_map>
@@ -66,15 +67,17 @@
  * Stores the deferred render operations and state used to compute ordering
  * for a single FBO/layer.
  */
-class LayerReorderer {
+class LayerBuilder {
+// Prevent copy/assign because users may stash pointer to offscreenBuffer and viewportClip
+PREVENT_COPY_AND_ASSIGN(LayerBuilder);
 public:
-    // Create LayerReorderer for Fbo0
-    LayerReorderer(uint32_t width, uint32_t height, const Rect& repaintRect)
-            : LayerReorderer(width, height, repaintRect, nullptr, nullptr) {};
+    // Create LayerBuilder for Fbo0
+    LayerBuilder(uint32_t width, uint32_t height, const Rect& repaintRect)
+            : LayerBuilder(width, height, repaintRect, nullptr, nullptr) {};
 
-    // Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
+    // Create LayerBuilder for an offscreen layer, where beginLayerOp is present for a
     // saveLayer, renderNode is present for a HW layer.
-    LayerReorderer(uint32_t width, uint32_t height,
+    LayerBuilder(uint32_t width, uint32_t height,
             const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
 
     // iterate back toward target to see if anything drawn since should overlap the new op
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
index be612d2..5b1a854 100644
--- a/libs/hwui/LayerUpdateQueue.h
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -42,7 +42,7 @@
     LayerUpdateQueue() {}
     void enqueueLayerWithDamage(RenderNode* renderNode, Rect dirty);
     void clear();
-    const std::vector<Entry> entries() const { return mEntries; }
+    const std::vector<Entry>& entries() const { return mEntries; }
 private:
     std::vector<Entry> mEntries;
 };
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 612cdfd..8e4a3df 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -47,11 +47,11 @@
 class DisplayListCanvas;
 class DisplayListOp;
 class OpenGLRenderer;
-class OpReorderer;
 class Rect;
 class SkiaShader;
 
 #if HWUI_NEW_OPS
+class FrameBuilder;
 class OffscreenBuffer;
 struct RenderNodeOp;
 typedef OffscreenBuffer layer_t;
@@ -87,7 +87,7 @@
  */
 class RenderNode : public VirtualLightRefBase {
 friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties
-friend class FrameReorderer;
+friend class FrameBuilder;
 public:
     enum DirtyPropertyMask {
         GENERIC         = 1 << 1,
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index 130cc80..976f775 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -162,15 +162,15 @@
     mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
 }
 
-SkCanvas::SaveLayerStrategy SkiaCanvasProxy::willSaveLayer(const SkRect* rectPtr,
-        const SkPaint* paint, SaveFlags flags) {
+SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(const SaveLayerRec& saveLayerRec) {
     SkRect rect;
-    if (rectPtr) {
-        rect = *rectPtr;
-    } else if(!mCanvas->getClipBounds(&rect)) {
+    if (saveLayerRec.fBounds) {
+        rect = *saveLayerRec.fBounds;
+    } else if (!mCanvas->getClipBounds(&rect)) {
         rect = SkRect::MakeEmpty();
     }
-    mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint, flags);
+    mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint,
+                       (SkCanvas::SaveFlags) SaveLayerFlagsToSaveFlags(saveLayerRec.fSaveLayerFlags));
     return SkCanvas::kNoLayer_SaveLayerStrategy;
 }
 
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
index 0089fb5..e342d19 100644
--- a/libs/hwui/SkiaCanvasProxy.h
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -47,7 +47,7 @@
     virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
 
     virtual void willSave() override;
-    virtual SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+    virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
     virtual void willRestore() override;
 
     virtual void didConcat(const SkMatrix&) override;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index fff8e09..644f356 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -31,7 +31,7 @@
 #include "utils/TimeUtils.h"
 
 #if HWUI_NEW_OPS
-#include "FrameReorderer.h"
+#include "FrameBuilder.h"
 #endif
 
 #include <cutils/properties.h>
@@ -338,14 +338,13 @@
     mEglManager.damageFrame(frame, dirty);
 
 #if HWUI_NEW_OPS
-    FrameReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
+    FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
             mRenderNodes, mLightCenter);
     mLayerUpdateQueue.clear();
     BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(),
             mOpaque, mLightInfo);
     // TODO: profiler().draw(mCanvas);
-    reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
-
+    frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
     bool drew = renderer.didDraw();
 
 #else
diff --git a/libs/hwui/tests/microbench/FrameReordererBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
similarity index 90%
rename from libs/hwui/tests/microbench/FrameReordererBench.cpp
rename to libs/hwui/tests/microbench/FrameBuilderBench.cpp
index b4c9a36..67c95e2 100644
--- a/libs/hwui/tests/microbench/FrameReordererBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -19,7 +19,7 @@
 #include "BakedOpState.h"
 #include "BakedOpDispatcher.h"
 #include "BakedOpRenderer.h"
-#include "FrameReorderer.h"
+#include "FrameBuilder.h"
 #include "LayerUpdateQueue.h"
 #include "RecordedOp.h"
 #include "RecordingCanvas.h"
@@ -66,9 +66,9 @@
     auto nodes = createTestNodeList();
     StartBenchmarkTiming();
     for (int i = 0; i < iters; i++) {
-        FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+        FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
                 nodes, sLightCenter);
-        MicroBench::DoNotOptimize(&reorderer);
+        MicroBench::DoNotOptimize(&frameBuilder);
     }
     StopBenchmarkTiming();
 }
@@ -84,11 +84,11 @@
 
         StartBenchmarkTiming();
         for (int i = 0; i < iters; i++) {
-            FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+            FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
                     nodes, sLightCenter);
 
             BakedOpRenderer renderer(caches, renderState, true, lightInfo);
-            reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
+            frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
             MicroBench::DoNotOptimize(&renderer);
         }
         StopBenchmarkTiming();
@@ -117,10 +117,10 @@
     auto nodes = getSyncedSceneNodes(sceneName);
     benchmark.StartBenchmarkTiming();
     for (int i = 0; i < iters; i++) {
-        FrameReorderer reorderer(sEmptyLayerUpdateQueue,
+        FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
                 SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
                 nodes, sLightCenter);
-        MicroBench::DoNotOptimize(&reorderer);
+        MicroBench::DoNotOptimize(&frameBuilder);
     }
     benchmark.StopBenchmarkTiming();
 }
@@ -136,12 +136,12 @@
 
         benchmark.StartBenchmarkTiming();
         for (int i = 0; i < iters; i++) {
-            FrameReorderer reorderer(sEmptyLayerUpdateQueue,
+            FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
                     SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
                     nodes, sLightCenter);
 
             BakedOpRenderer renderer(caches, renderState, true, lightInfo);
-            reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
+            frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
             MicroBench::DoNotOptimize(&renderer);
         }
         benchmark.StopBenchmarkTiming();
diff --git a/libs/hwui/tests/unit/BakedOpStateTests.cpp b/libs/hwui/tests/unit/BakedOpStateTests.cpp
index b350686..0f8e047 100644
--- a/libs/hwui/tests/unit/BakedOpStateTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpStateTests.cpp
@@ -199,9 +199,12 @@
 }
 
 TEST(BakedOpState, tryShadowOpConstruct) {
+    Matrix4 translate10x20;
+    translate10x20.loadTranslate(10, 20, 0);
+
     LinearAllocator allocator;
     {
-        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
+        auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect()); // Note: empty clip
         BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
 
         EXPECT_EQ(nullptr, bakedState) << "op should be rejected by clip, so not constructed";
@@ -209,11 +212,14 @@
                 "since op is quick rejected based on snapshot clip";
     }
     {
-        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+        auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
         BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
 
         ASSERT_NE(nullptr, bakedState) << "NOT rejected by clip, so op should be constructed";
         EXPECT_LE(64u, allocator.usedSize()) << "relatively large alloc for non-rejected op";
+
+        EXPECT_MATRIX_APPROX_EQ(translate10x20, bakedState->computedState.transform);
+        EXPECT_EQ(Rect(100, 200), bakedState->computedState.clippedBounds);
     }
 }
 
diff --git a/libs/hwui/tests/unit/FrameReordererTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
similarity index 90%
rename from libs/hwui/tests/unit/FrameReordererTests.cpp
rename to libs/hwui/tests/unit/FrameBuilderTests.cpp
index 9d2eb98..bded50a 100644
--- a/libs/hwui/tests/unit/FrameReordererTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -18,7 +18,7 @@
 
 #include <BakedOpState.h>
 #include <DeferredLayerUpdater.h>
-#include <FrameReorderer.h>
+#include <FrameBuilder.h>
 #include <LayerUpdateQueue.h>
 #include <RecordedOp.h>
 #include <RecordingCanvas.h>
@@ -113,7 +113,7 @@
 
 class FailRenderer : public TestRendererBase {};
 
-TEST(FrameReorderer, simple) {
+TEST(FrameBuilder, simple) {
     class SimpleTestRenderer : public TestRendererBase {
     public:
         void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -138,14 +138,14 @@
         canvas.drawRect(0, 0, 100, 200, SkPaint());
         canvas.drawBitmap(bitmap, 10, 10, nullptr);
     });
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
             createSyncedNodeList(node), sLightCenter);
     SimpleTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
 }
 
-TEST(FrameReorderer, simpleStroke) {
+TEST(FrameBuilder, simpleStroke) {
     class SimpleStrokeTestRenderer : public TestRendererBase {
     public:
         void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
@@ -164,14 +164,14 @@
         strokedPaint.setStrokeWidth(10);
         canvas.drawPoint(50, 50, strokedPaint);
     });
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
             createSyncedNodeList(node), sLightCenter);
     SimpleStrokeTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-TEST(FrameReorderer, simpleRejection) {
+TEST(FrameBuilder, simpleRejection) {
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -179,14 +179,14 @@
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
     });
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
 
     FailRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(FrameReorderer, simpleBatching) {
+TEST(FrameBuilder, simpleBatching) {
     const int LOOPS = 5;
     class SimpleBatchingTestRenderer : public TestRendererBase {
     public:
@@ -214,15 +214,15 @@
         canvas.restore();
     });
 
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
     SimpleBatchingTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2 * LOOPS, renderer.getIndex())
             << "Expect number of ops = 2 * loop count";
 }
 
-TEST(FrameReorderer, clippedMerging) {
+TEST(FrameBuilder, clippedMerging) {
     class ClippedMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedBitmapOps(const MergedBakedOpList& opList) override {
@@ -255,14 +255,14 @@
         canvas.drawBitmap(bitmap, 40, 70, nullptr);
     });
 
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
             createSyncedNodeList(node), sLightCenter);
     ClippedMergingTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameReorderer, textMerging) {
+TEST(FrameBuilder, textMerging) {
     class TextMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -283,14 +283,14 @@
         TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
         TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
     });
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
             createSyncedNodeList(node), sLightCenter);
     TextMergingTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
 }
 
-TEST(FrameReorderer, textStrikethrough) {
+TEST(FrameBuilder, textStrikethrough) {
     const int LOOPS = 5;
     class TextStrikethroughTestRenderer : public TestRendererBase {
     public:
@@ -314,15 +314,15 @@
             TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
         }
     });
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
             createSyncedNodeList(node), sLightCenter);
     TextStrikethroughTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2 * LOOPS, renderer.getIndex())
             << "Expect number of ops = 2 * loop count";
 }
 
-RENDERTHREAD_TEST(FrameReorderer, textureLayer) {
+RENDERTHREAD_TEST(FrameBuilder, textureLayer) {
     class TextureLayerTestRenderer : public TestRendererBase {
     public:
         void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
@@ -348,14 +348,14 @@
         canvas.drawLayer(layerUpdater.get());
         canvas.restore();
     });
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
     TextureLayerTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-TEST(FrameReorderer, renderNode) {
+TEST(FrameBuilder, renderNode) {
     class RenderNodeTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -393,13 +393,13 @@
         canvas.restore();
     });
 
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(parent), sLightCenter);
     RenderNodeTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(FrameReorderer, clipped) {
+TEST(FrameBuilder, clipped) {
     class ClippedTestRenderer : public TestRendererBase {
     public:
         void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
@@ -416,14 +416,14 @@
         canvas.drawBitmap(bitmap, 0, 0, nullptr);
     });
 
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
             SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
             200, 200, createSyncedNodeList(node), sLightCenter);
     ClippedTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(FrameReorderer, saveLayer_simple) {
+TEST(FrameBuilder, saveLayer_simple) {
     class SaveLayerSimpleTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -459,14 +459,14 @@
         canvas.drawRect(10, 10, 190, 190, SkPaint());
         canvas.restore();
     });
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
     SaveLayerSimpleTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameReorderer, saveLayer_nested) {
+TEST(FrameBuilder, saveLayer_nested) {
     /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
      * - startTemporaryLayer2, rect2 endLayer2
      * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
@@ -531,14 +531,14 @@
         canvas.restore();
     });
 
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
             createSyncedNodeList(node), sLightCenter);
     SaveLayerNestedTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex());
 }
 
-TEST(FrameReorderer, saveLayer_contentRejection) {
+TEST(FrameBuilder, saveLayer_contentRejection) {
         auto node = TestUtils::createNode(0, 0, 200, 200,
                 [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -551,15 +551,15 @@
         canvas.restore();
         canvas.restore();
     });
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
 
     FailRenderer renderer;
     // should see no ops, even within the layer, since the layer should be rejected
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(FrameReorderer, saveLayerUnclipped_simple) {
+TEST(FrameBuilder, saveLayerUnclipped_simple) {
     class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -594,14 +594,14 @@
         canvas.drawRect(0, 0, 200, 200, SkPaint());
         canvas.restore();
     });
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
     SaveLayerUnclippedSimpleTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameReorderer, saveLayerUnclipped_mergedClears) {
+TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
     class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -648,10 +648,10 @@
         canvas.drawRect(0, 0, 100, 100, SkPaint());
         canvas.restoreToCount(restoreTo);
     });
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
     SaveLayerUnclippedMergedClearsTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex())
             << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
 }
@@ -660,7 +660,7 @@
  * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
  * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
  */
-TEST(FrameReorderer, saveLayerUnclipped_complex) {
+TEST(FrameBuilder, saveLayerUnclipped_complex) {
     class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
@@ -710,14 +710,14 @@
         canvas.restore();
         canvas.restore();
     });
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
             createSyncedNodeList(node), sLightCenter);
     SaveLayerUnclippedComplexTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(12, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameReorderer, hwLayer_simple) {
+RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
     class HwLayerSimpleTestRenderer : public TestRendererBase {
     public:
         void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -768,17 +768,17 @@
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
 
-    FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             syncedNodeList, sLightCenter);
     HwLayerSimpleTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(6, renderer.getIndex());
 
     // clean up layer pointer, so we can safely destruct RenderNode
     *layerHandle = nullptr;
 }
 
-RENDERTHREAD_TEST(FrameReorderer, hwLayer_complex) {
+RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
     /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
      * - startRepaintLayer(child), rect(grey), endLayer
      * - startTemporaryLayer, drawLayer(child), endLayer
@@ -869,10 +869,10 @@
     layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
     layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
 
-    FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             syncedList, sLightCenter);
     HwLayerComplexTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(13, renderer.getIndex());
 
     // clean up layer pointers, so we can safely destruct RenderNodes
@@ -894,7 +894,7 @@
     node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
     canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
 }
-TEST(FrameReorderer, zReorder) {
+TEST(FrameBuilder, zReorder) {
     class ZReorderTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -918,14 +918,14 @@
         drawOrderedRect(&canvas, 8);
         drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
     });
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
             createSyncedNodeList(parent), sLightCenter);
     ZReorderTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex());
 };
 
-TEST(FrameReorderer, projectionReorder) {
+TEST(FrameBuilder, projectionReorder) {
     static const int scrollX = 5;
     static const int scrollY = 10;
     class ProjectionReorderTestRenderer : public TestRendererBase {
@@ -1001,10 +1001,10 @@
         canvas.restore();
     });
 
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
             createSyncedNodeList(parent), sLightCenter);
     ProjectionReorderTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(3, renderer.getIndex());
 }
 
@@ -1020,7 +1020,7 @@
     });
 }
 
-TEST(FrameReorderer, shadow) {
+TEST(FrameBuilder, shadow) {
     class ShadowTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1044,14 +1044,14 @@
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
     });
 
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(parent), sLightCenter);
     ShadowTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-TEST(FrameReorderer, shadowSaveLayer) {
+TEST(FrameBuilder, shadowSaveLayer) {
     class ShadowSaveLayerTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -1085,14 +1085,14 @@
         canvas.restoreToCount(count);
     });
 
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(parent), (Vector3) { 100, 100, 100 });
     ShadowSaveLayerTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(5, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameReorderer, shadowHwLayer) {
+RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
     class ShadowHwLayerTestRenderer : public TestRendererBase {
     public:
         void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1135,17 +1135,17 @@
     auto syncedList = createSyncedNodeList(parent);
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
-    FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             syncedList, (Vector3) { 100, 100, 100 });
     ShadowHwLayerTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(5, renderer.getIndex());
 
     // clean up layer pointer, so we can safely destruct RenderNode
     *layerHandle = nullptr;
 }
 
-TEST(FrameReorderer, shadowLayering) {
+TEST(FrameBuilder, shadowLayering) {
     class ShadowLayeringTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1164,10 +1164,10 @@
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
     });
 
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(parent), sLightCenter);
     ShadowLayeringTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
@@ -1192,14 +1192,14 @@
         canvas.drawRect(0, 0, 100, 100, paint);
     });
 
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
             createSyncedNodeList(node), sLightCenter);
     PropertyTestRenderer renderer(opValidateCallback);
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
 }
 
-TEST(FrameReorderer, renderPropOverlappingRenderingAlpha) {
+TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
     testProperty([](RenderProperties& properties) {
         properties.setAlpha(0.5f);
         properties.setHasOverlappingRendering(false);
@@ -1208,7 +1208,7 @@
     });
 }
 
-TEST(FrameReorderer, renderPropClipping) {
+TEST(FrameBuilder, renderPropClipping) {
     testProperty([](RenderProperties& properties) {
         properties.setClipToBounds(true);
         properties.setClipBounds(Rect(10, 20, 300, 400));
@@ -1218,7 +1218,7 @@
     });
 }
 
-TEST(FrameReorderer, renderPropRevealClip) {
+TEST(FrameBuilder, renderPropRevealClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableRevealClip().set(true, 50, 50, 25);
     }, [](const RectOp& op, const BakedOpState& state) {
@@ -1229,7 +1229,7 @@
     });
 }
 
-TEST(FrameReorderer, renderPropOutlineClip) {
+TEST(FrameBuilder, renderPropOutlineClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableOutline().setShouldClip(true);
         properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
@@ -1241,7 +1241,7 @@
     });
 }
 
-TEST(FrameReorderer, renderPropTransform) {
+TEST(FrameBuilder, renderPropTransform) {
     testProperty([](RenderProperties& properties) {
         properties.setLeftTopRightBottom(10, 10, 110, 110);
 
@@ -1334,15 +1334,15 @@
     });
     auto nodes = createSyncedNodeList(node); // sync before querying height
 
-    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
     SaveLayerAlphaClipTestRenderer renderer(outObservedData);
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 
     // assert, since output won't be valid if we haven't seen a save layer triggered
     ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
 }
 
-TEST(FrameReorderer, renderPropSaveLayerAlphaClipBig) {
+TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         properties.setTranslationX(10); // offset rendering content
@@ -1358,7 +1358,7 @@
             << "expect content to be translated as part of being clipped";
 }
 
-TEST(FrameReorderer, renderPropSaveLayerAlphaRotate) {
+TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         // Translate and rotate the view so that the only visible part is the top left corner of
@@ -1377,7 +1377,7 @@
     EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
 }
 
-TEST(FrameReorderer, renderPropSaveLayerAlphaScale) {
+TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         properties.setPivotX(0);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a092408..ea1690f 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -51,6 +51,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 
 /**
  * AudioManager provides access to volume and ringer mode control.
@@ -2158,36 +2159,73 @@
     }
 
     /**
-     * Handler for audio focus events coming from the audio service.
+     * Handler for events (audio focus change, recording config change) coming from the
+     * audio service.
      */
-    private final FocusEventHandlerDelegate mAudioFocusEventHandlerDelegate =
-            new FocusEventHandlerDelegate();
+    private final ServiceEventHandlerDelegate mServiceEventHandlerDelegate =
+            new ServiceEventHandlerDelegate();
 
     /**
-     * Helper class to handle the forwarding of audio focus events to the appropriate listener
+     * Event types
      */
-    private class FocusEventHandlerDelegate {
+    private final static int MSSG_FOCUS_CHANGE = 0;
+    private final static int MSSG_RECORDING_CONFIG_CHANGE = 1;
+
+    /**
+     * Helper class to handle the forwarding of audio service events to the appropriate listener
+     */
+    private class ServiceEventHandlerDelegate {
         private final Handler mHandler;
 
-        FocusEventHandlerDelegate() {
+        ServiceEventHandlerDelegate() {
             Looper looper;
             if ((looper = Looper.myLooper()) == null) {
                 looper = Looper.getMainLooper();
             }
 
             if (looper != null) {
-                // implement the event handler delegate to receive audio focus events
+                // implement the event handler delegate to receive events from audio service
                 mHandler = new Handler(looper) {
                     @Override
                     public void handleMessage(Message msg) {
-                        OnAudioFocusChangeListener listener = null;
-                        synchronized(mFocusListenerLock) {
-                            listener = findFocusListener((String)msg.obj);
-                        }
-                        if (listener != null) {
-                            Log.d(TAG, "AudioManager dispatching onAudioFocusChange("
-                                    + msg.what + ") for " + msg.obj);
-                            listener.onAudioFocusChange(msg.what);
+                        switch (msg.what) {
+                            case MSSG_FOCUS_CHANGE:
+                                OnAudioFocusChangeListener listener = null;
+                                synchronized(mFocusListenerLock) {
+                                    listener = findFocusListener((String)msg.obj);
+                                }
+                                if (listener != null) {
+                                    Log.d(TAG, "AudioManager dispatching onAudioFocusChange("
+                                            + msg.what + ") for " + msg.obj);
+                                    listener.onAudioFocusChange(msg.arg1);
+                                }
+                                break;
+                            case MSSG_RECORDING_CONFIG_CHANGE:
+                                // optimizing for the case of a single callback
+                                AudioRecordingCallback singleCallback = null;
+                                ArrayList<AudioRecordingCallback> multipleCallbacks = null;
+                                synchronized(mRecordCallbackLock) {
+                                    if ((mRecordCallbackList != null)
+                                            && (mRecordCallbackList.size() != 0)) {
+                                        if (mRecordCallbackList.size() == 1) {
+                                            singleCallback = mRecordCallbackList.get(0);
+                                        } else {
+                                            multipleCallbacks =
+                                                    new ArrayList<AudioRecordingCallback>(
+                                                            mRecordCallbackList);
+                                        }
+                                    }
+                                }
+                                if (singleCallback != null) {
+                                    singleCallback.onRecordConfigChanged();
+                                } else if (multipleCallbacks != null) {
+                                    for (int i=0 ; i < multipleCallbacks.size() ; i++) {
+                                        multipleCallbacks.get(i).onRecordConfigChanged();
+                                    }
+                                }
+                                break;
+                            default:
+                                Log.e(TAG, "Unknown event " + msg.what);
                         }
                     }
                 };
@@ -2204,8 +2242,9 @@
     private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {
 
         public void dispatchAudioFocusChange(int focusChange, String id) {
-            Message m = mAudioFocusEventHandlerDelegate.getHandler().obtainMessage(focusChange, id);
-            mAudioFocusEventHandlerDelegate.getHandler().sendMessage(m);
+            final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage(
+                    MSSG_FOCUS_CHANGE/*what*/, focusChange/*arg1*/, 0/*arg2 ignored*/, id/*obj*/);
+            mServiceEventHandlerDelegate.getHandler().sendMessage(m);
         }
 
     };
@@ -2702,6 +2741,8 @@
     }
 
 
+    //====================================================================
+    // Audio policy
     /**
      * @hide
      * Register the given {@link AudioPolicy}.
@@ -2754,6 +2795,131 @@
     }
 
 
+    //====================================================================
+    // Recording configuration
+    /**
+     * @hide
+     * candidate for public API
+     */
+    public static abstract class AudioRecordingCallback {
+        /**
+         * @hide
+         * candidate for public API
+         */
+        public void onRecordConfigChanged() {}
+    }
+
+    /**
+     * @hide
+     * candidate for public API
+     * @param non-null callback
+     */
+    public void registerAudioRecordingCallback(@NonNull AudioRecordingCallback cb) {
+        if (cb == null) {
+            throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
+        }
+        synchronized(mRecordCallbackLock) {
+            // lazy initialization of the list of recording callbacks
+            if (mRecordCallbackList == null) {
+                mRecordCallbackList = new ArrayList<AudioRecordingCallback>();
+            }
+            final int oldCbCount = mRecordCallbackList.size();
+            if (!mRecordCallbackList.contains(cb)) {
+                mRecordCallbackList.add(cb);
+                final int newCbCount = mRecordCallbackList.size();
+                if ((oldCbCount == 0) && (newCbCount > 0)) {
+                    // register binder for callbacks
+                    final IAudioService service = getService();
+                    try {
+                        service.registerRecordingCallback(mRecCb);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Dead object in registerRecordingCallback", e);
+                    }
+                }
+            } else {
+                Log.w(TAG, "attempt to call registerAudioRecordingCallback() on a previously"
+                        + "registered callback");
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * candidate for public API
+     * @param non-null callback
+     */
+    public void unregisterAudioRecordingCallback(@NonNull AudioRecordingCallback cb) {
+        if (cb == null) {
+            throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
+        }
+        synchronized(mRecordCallbackLock) {
+            if (mRecordCallbackList == null) {
+                return;
+            }
+            final int oldCbCount = mRecordCallbackList.size();
+            if (mRecordCallbackList.remove(cb)) {
+                final int newCbCount = mRecordCallbackList.size();
+                if ((oldCbCount > 0) && (newCbCount == 0)) {
+                    // unregister binder for callbacks
+                    final IAudioService service = getService();
+                    try {
+                        service.unregisterRecordingCallback(mRecCb);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Dead object in unregisterRecordingCallback", e);
+                    }
+                }
+            } else {
+                Log.w(TAG, "attempt to call unregisterAudioRecordingCallback() on a callback"
+                        + " already unregistered or never registered");
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * candidate for public API
+     * @return a non-null array of recording configurations. An array of length 0 indicates there is
+     *     no recording active when queried.
+     */
+    public @NonNull AudioRecordConfiguration[] getActiveRecordConfigurations() {
+        final IAudioService service = getService();
+        try {
+            return service.getActiveRecordConfigurations();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to retrieve active record configurations", e);
+            return null;
+        }
+    }
+
+    /**
+     * constants for the recording events, to keep in sync
+     * with frameworks/av/include/media/AudioPolicy.h
+     */
+    /** @hide */
+    public final static int RECORD_CONFIG_EVENT_START = 1;
+    /** @hide */
+    public final static int RECORD_CONFIG_EVENT_STOP = 0;
+
+    /**
+     * All operations on this list are sync'd on mRecordCallbackLock.
+     * List is lazy-initialized in {@link #registerAudioRecordingCallback(AudioRecordingCallback)}.
+     * List can be null.
+     */
+    private List<AudioRecordingCallback> mRecordCallbackList;
+    private final Object mRecordCallbackLock = new Object();
+
+    private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
+
+        public void dispatchRecordingConfigChange() {
+            final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage(
+                    MSSG_RECORDING_CONFIG_CHANGE/*what*/);
+            mServiceEventHandlerDelegate.getHandler().sendMessage(m);
+        }
+
+    };
+
+    //=====================================================================
+
     /**
      *  @hide
      *  Reload audio settings. This method is called by Settings backup
diff --git a/media/java/android/media/AudioRecordConfiguration.aidl b/media/java/android/media/AudioRecordConfiguration.aidl
new file mode 100644
index 0000000..afe912b
--- /dev/null
+++ b/media/java/android/media/AudioRecordConfiguration.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media;
+
+parcelable AudioRecordConfiguration;
diff --git a/media/java/android/media/AudioRecordConfiguration.java b/media/java/android/media/AudioRecordConfiguration.java
new file mode 100644
index 0000000..aefe692
--- /dev/null
+++ b/media/java/android/media/AudioRecordConfiguration.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ * Candidate for public API, see AudioManager.getActiveRecordConfiguration()
+ *
+ */
+public class AudioRecordConfiguration implements Parcelable {
+
+    private final int mSessionId;
+
+    private final int mClientSource;
+
+    /**
+     * @hide
+     */
+    public AudioRecordConfiguration(int session, int source) {
+        mSessionId = session;
+        mClientSource = source;
+    }
+
+    /**
+     * @return one of AudioSource.MIC, AudioSource.VOICE_UPLINK,
+     *       AudioSource.VOICE_DOWNLINK, AudioSource.VOICE_CALL,
+     *       AudioSource.CAMCORDER, AudioSource.VOICE_RECOGNITION,
+     *       AudioSource.VOICE_COMMUNICATION.
+     */
+    public int getClientAudioSource() { return mClientSource; }
+
+    /**
+     * @return the session number of the recorder.
+     */
+    public int getAudioSessionId() { return mSessionId; }
+
+
+    public static final Parcelable.Creator<AudioRecordConfiguration> CREATOR
+            = new Parcelable.Creator<AudioRecordConfiguration>() {
+        /**
+         * Rebuilds an AudioRecordConfiguration previously stored with writeToParcel().
+         * @param p Parcel object to read the AudioRecordConfiguration from
+         * @return a new AudioRecordConfiguration created from the data in the parcel
+         */
+        public AudioRecordConfiguration createFromParcel(Parcel p) {
+            return new AudioRecordConfiguration(p);
+        }
+        public AudioRecordConfiguration[] newArray(int size) {
+            return new AudioRecordConfiguration[size];
+        }
+    };
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSessionId, mClientSource);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSessionId);
+        dest.writeInt(mClientSource);
+    }
+
+    private AudioRecordConfiguration(Parcel in) {
+        mSessionId = in.readInt();
+        mClientSource = in.readInt();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || !(o instanceof AudioRecordConfiguration)) return false;
+
+        final AudioRecordConfiguration that = (AudioRecordConfiguration) o;
+         return ((mSessionId == that.mSessionId)
+                 && (mClientSource == that.mClientSource));
+    }
+}
\ No newline at end of file
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 7bfd7ca..aa0d78d 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -227,7 +227,7 @@
     }
 
     /**
-     * Handles events for the audio policy manager about dynamic audio policies
+     * Handles events from the audio policy manager about dynamic audio policies
      * @see android.media.audiopolicy.AudioPolicy
      */
     public interface DynamicPolicyCallback
@@ -267,6 +267,33 @@
         }
     }
 
+    /**
+     * Handles events from the audio policy manager about recording events
+     * @see android.media.AudioManager.AudioRecordingCallback
+     */
+    public interface AudioRecordingCallback
+    {
+        void onRecordingConfigurationChanged(int event, int session, int source);
+    }
+
+    private static AudioRecordingCallback sRecordingCallback;
+
+    public static void setRecordingCallback(AudioRecordingCallback cb) {
+        synchronized (AudioSystem.class) {
+            sRecordingCallback = cb;
+            native_register_recording_callback();
+        }
+    }
+
+    private static void recordingCallbackFromNative(int event, int session, int source) {
+        AudioRecordingCallback cb = null;
+        synchronized (AudioSystem.class) {
+            cb = sRecordingCallback;
+        }
+        if (cb != null) {
+            cb.onRecordingConfigurationChanged(event, session, source);
+        }
+    }
 
     /*
      * Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...)
@@ -646,6 +673,8 @@
 
     // declare this instance as having a dynamic policy callback handler
     private static native final void native_register_dynamic_policy_callback();
+    // declare this instance as having a recording configuration update callback handler
+    private static native final void native_register_recording_callback();
 
     // must be kept in sync with value in include/system/audio.h
     public static final int AUDIO_HW_SYNC_INVALID = 0;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index dbb7661..abe92c7 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -20,9 +20,11 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.ComponentName;
 import android.media.AudioAttributes;
+import android.media.AudioRecordConfiguration;
 import android.media.AudioRoutesInfo;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioRoutesObserver;
+import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
 import android.media.IVolumeController;
 import android.media.Rating;
@@ -161,4 +163,10 @@
     int setFocusPropertiesForPolicy(int duckingBehavior, in IAudioPolicyCallback pcb);
 
     void setVolumePolicy(in VolumePolicy policy);
+
+    void registerRecordingCallback(in IRecordingConfigDispatcher rcdb);
+
+    oneway void unregisterRecordingCallback(in IRecordingConfigDispatcher rcdb);
+
+    AudioRecordConfiguration[] getActiveRecordConfigurations();
 }
diff --git a/media/java/android/media/IRecordingConfigDispatcher.aidl b/media/java/android/media/IRecordingConfigDispatcher.aidl
new file mode 100644
index 0000000..a5eb8b9f
--- /dev/null
+++ b/media/java/android/media/IRecordingConfigDispatcher.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * AIDL for the RecordingActivity monitor in AudioService to signal audio recording updates.
+ *
+ * {@hide}
+ */
+oneway interface IRecordingConfigDispatcher {
+
+    void dispatchRecordingConfigChange();
+
+}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 478fd99..f1f8437 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2175,8 +2175,6 @@
             int offset, int size, long presentationTimeUs, int flags)
         throws CryptoException;
 
-    // The following mode constants MUST stay in sync with their equivalents
-    // in media/hardware/CryptoAPI.h !
     public static final int CRYPTO_MODE_UNENCRYPTED = 0;
     public static final int CRYPTO_MODE_AES_CTR     = 1;
     public static final int CRYPTO_MODE_AES_CBC     = 2;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 9bcb5e3..0fba992 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1735,8 +1735,7 @@
             CodecProfileLevel[] profileLevels = mParent.profileLevels;
             String mime = mParent.getMimeType();
 
-            if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC) ||
-                    mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_DOLBY_AVC)) {
+            if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC)) {
                 maxBlocks = 99;
                 maxBlocksPerSecond = 1485;
                 maxBps = 64000;
@@ -2090,8 +2089,7 @@
                 applyMacroBlockLimits(Short.MAX_VALUE, Short.MAX_VALUE,
                         maxBlocks, maxBlocksPerSecond, blockSize, blockSize,
                         1 /* widthAlignment */, 1 /* heightAlignment */);
-            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC) ||
-                    mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_DOLBY_HEVC)) {
+            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
                 maxBlocks = 36864;
                 maxBlocksPerSecond = maxBlocks * 15;
                 maxBps = 128000;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index a102e51..b2fa0ac 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -92,8 +92,6 @@
     public static final String MIMETYPE_VIDEO_H263 = "video/3gpp";
     public static final String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
     public static final String MIMETYPE_VIDEO_RAW = "video/raw";
-    public static final String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
-    public static final String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
 
     public static final String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
     public static final String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
diff --git a/media/java/android/mtp/MtpEvent.java b/media/java/android/mtp/MtpEvent.java
index 6ec16db..dc89a56 100644
--- a/media/java/android/mtp/MtpEvent.java
+++ b/media/java/android/mtp/MtpEvent.java
@@ -18,15 +18,152 @@
 
 /**
  * This class encapsulates information about a MTP event.
- * Event constants are defined by the USB-IF MTP specification.
+ * This corresponds to the events described in appendix G of the MTP specification.
  */
 public class MtpEvent {
     private int mEventCode = MtpConstants.EVENT_UNDEFINED;
 
+    // Parameters for event. The interpretation of event parameters depends upon mEventCode.
+    private int mParameter1;
+    private int mParameter2;
+    private int mParameter3;
+
     /**
      * Returns event code of MTP event.
      * See the USB-IF MTP specification for the details of event constants.
      * @return event code
      */
     public int getEventCode() { return mEventCode; }
+
+    /**
+     * Obtains the first event parameter.
+     */
+    public int getParameter1() { return mParameter1; }
+
+    /**
+     * Obtains the second event parameter.
+     */
+    public int getParameter2() { return mParameter2; }
+
+    /**
+     * Obtains the third event parameter.
+     */
+    public int getParameter3() { return mParameter3; }
+
+    /**
+     * Obtains objectHandle event parameter.
+     *
+     * @see MtpConstants#EVENT_OBJECT_ADDED
+     * @see MtpConstants#EVENT_OBJECT_REMOVED
+     * @see MtpConstants#EVENT_OBJECT_INFO_CHANGED
+     * @see MtpConstants#EVENT_REQUEST_OBJECT_TRANSFER
+     * @see MtpConstants#EVENT_OBJECT_PROP_CHANGED
+     * @see MtpConstants#EVENT_OBJECT_REFERENCES_CHANGED
+     */
+    public int getObjectHandle() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_OBJECT_ADDED:
+                return mParameter1;
+            case MtpConstants.EVENT_OBJECT_REMOVED:
+                return mParameter1;
+            case MtpConstants.EVENT_OBJECT_INFO_CHANGED:
+                return mParameter1;
+            case MtpConstants.EVENT_REQUEST_OBJECT_TRANSFER:
+                return mParameter1;
+            case MtpConstants.EVENT_OBJECT_PROP_CHANGED:
+                return mParameter1;
+            case MtpConstants.EVENT_OBJECT_REFERENCES_CHANGED:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("objectHandle", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains storageID event parameter.
+     *
+     * @see MtpConstants#EVENT_STORE_ADDED
+     * @see MtpConstants#EVENT_STORE_REMOVED
+     * @see MtpConstants#EVENT_STORE_FULL
+     * @see MtpConstants#EVENT_STORAGE_INFO_CHANGED
+     */
+    public int getStorageId() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_STORE_ADDED:
+                return mParameter1;
+            case MtpConstants.EVENT_STORE_REMOVED:
+                return mParameter1;
+            case MtpConstants.EVENT_STORE_FULL:
+                return mParameter1;
+            case MtpConstants.EVENT_STORAGE_INFO_CHANGED:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("storageID", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains devicePropCode event parameter.
+     *
+     * @see MtpConstants#EVENT_DEVICE_PROP_CHANGED
+     */
+    public int getDevicePropCode() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_DEVICE_PROP_CHANGED:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("devicePropCode", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains transactionID event parameter.
+     *
+     * @see MtpConstants#EVENT_CAPTURE_COMPLETE
+     */
+    public int getTransactionId() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_CAPTURE_COMPLETE:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("transactionID", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains objectPropCode event parameter.
+     *
+     * @see MtpConstants#EVENT_OBJECT_PROP_CHANGED
+     * @see MtpConstants#EVENT_OBJECT_PROP_DESC_CHANGED
+     */
+    public int getObjectPropCode() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_OBJECT_PROP_CHANGED:
+                return mParameter2;
+            case MtpConstants.EVENT_OBJECT_PROP_DESC_CHANGED:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("objectPropCode", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains objectFormatCode event parameter.
+     *
+     * @see MtpConstants#EVENT_OBJECT_PROP_DESC_CHANGED
+     */
+    public int getObjectFormatCode() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_OBJECT_PROP_DESC_CHANGED:
+                return mParameter2;
+            default:
+                throw new IllegalParameterAccess("objectFormatCode", mEventCode);
+        }
+    }
+
+    private static class IllegalParameterAccess extends UnsupportedOperationException {
+        public IllegalParameterAccess(String propertyName, int eventCode) {
+            super("Cannot obtain " + propertyName + " for the event: " + eventCode + ".");
+        }
+    }
 }
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 49b579c..2004a3a 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -85,6 +85,13 @@
     jmethodID setNativeObjectLocked;
 } gPersistentSurfaceClassInfo;
 
+static struct {
+    jint Unencrypted;
+    jint AesCtr;
+    jint AesCbc;
+} gCryptoModes;
+
+
 struct fields_t {
     jfieldID context;
     jmethodID postEventFromNativeID;
@@ -94,6 +101,9 @@
     jfieldID cryptoInfoKeyID;
     jfieldID cryptoInfoIVID;
     jfieldID cryptoInfoModeID;
+    jfieldID cryptoInfoPatternID;
+    jfieldID patternEncryptBlocksID;
+    jfieldID patternSkipBlocksID;
 };
 
 static fields_t gFields;
@@ -325,11 +335,12 @@
         const uint8_t key[16],
         const uint8_t iv[16],
         CryptoPlugin::Mode mode,
+        const CryptoPlugin::Pattern &pattern,
         int64_t presentationTimeUs,
         uint32_t flags,
         AString *errorDetailMsg) {
     return mCodec->queueSecureInputBuffer(
-            index, offset, subSamples, numSubSamples, key, iv, mode,
+            index, offset, subSamples, numSubSamples, key, iv, mode, pattern,
             presentationTimeUs, flags, errorDetailMsg);
 }
 
@@ -1275,7 +1286,26 @@
     jbyteArray ivObj =
         (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
 
-    jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
+    jint jmode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
+    enum CryptoPlugin::Mode mode;
+    if (jmode == gCryptoModes.Unencrypted) {
+        mode = CryptoPlugin::kMode_Unencrypted;
+    } else if (jmode == gCryptoModes.AesCtr) {
+        mode = CryptoPlugin::kMode_AES_CTR;
+    } else if (jmode == gCryptoModes.AesCbc) {
+        mode = CryptoPlugin::kMode_AES_CBC;
+    }  else {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    jobject patternObj = env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID);
+
+    CryptoPlugin::Pattern pattern;
+    if (patternObj != NULL) {
+        pattern.mEncryptBlocks = env->GetIntField(patternObj, gFields.patternEncryptBlocksID);
+        pattern.mSkipBlocks = env->GetIntField(patternObj, gFields.patternSkipBlocksID);
+    }
 
     status_t err = OK;
 
@@ -1360,7 +1390,8 @@
                 index, offset,
                 subSamples, numSubSamples,
                 (const uint8_t *)key, (const uint8_t *)iv,
-                (CryptoPlugin::Mode)mode,
+                mode,
+                pattern,
                 timestampUs,
                 flags,
                 &errorDetailMsg);
@@ -1658,6 +1689,22 @@
 
     CHECK(gFields.postEventFromNativeID != NULL);
 
+    jfieldID field;
+    field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_UNENCRYPTED", "I");
+    CHECK(field != NULL);
+    gCryptoModes.Unencrypted =
+        env->GetStaticIntField(clazz.get(), field);
+
+    field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_AES_CTR", "I");
+    CHECK(field != NULL);
+    gCryptoModes.AesCtr =
+        env->GetStaticIntField(clazz.get(), field);
+
+    field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_AES_CBC", "I");
+    CHECK(field != NULL);
+    gCryptoModes.AesCbc =
+        env->GetStaticIntField(clazz.get(), field);
+
     clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
     CHECK(clazz.get() != NULL);
 
@@ -1682,10 +1729,22 @@
     gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
     CHECK(gFields.cryptoInfoModeID != NULL);
 
+    gFields.cryptoInfoPatternID = env->GetFieldID(clazz.get(), "pattern",
+        "Landroid/media/MediaCodec$CryptoInfo$Pattern;");
+    CHECK(gFields.cryptoInfoPatternID != NULL);
+
+    clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo$Pattern"));
+    CHECK(clazz.get() != NULL);
+
+    gFields.patternEncryptBlocksID = env->GetFieldID(clazz.get(), "mEncryptBlocks", "I");
+    CHECK(gFields.patternEncryptBlocksID != NULL);
+
+    gFields.patternSkipBlocksID = env->GetFieldID(clazz.get(), "mSkipBlocks", "I");
+    CHECK(gFields.patternSkipBlocksID != NULL);
+
     clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
     CHECK(clazz.get() != NULL);
 
-    jfieldID field;
     field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I");
     CHECK(field != NULL);
     gCryptoErrorCodes.cryptoErrorNoKey =
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 6650cf9..c0c47ef 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -81,6 +81,7 @@
             const uint8_t key[16],
             const uint8_t iv[16],
             CryptoPlugin::Mode mode,
+            const CryptoPlugin::Pattern &pattern,
             int64_t presentationTimeUs,
             uint32_t flags,
             AString *errorDetailMsg);
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 4aa12c2..130dfe5 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -98,6 +98,9 @@
 
 // MtpEvent fields
 static jfieldID field_event_eventCode;
+static jfieldID field_event_parameter1;
+static jfieldID field_event_parameter2;
+static jfieldID field_event_parameter3;
 
 class JavaArrayWriter {
 public:
@@ -573,13 +576,17 @@
         env->ThrowNew(clazz_io_exception, "");
         return NULL;
     }
-    const int eventCode = device->reapEventRequest(seq);
+    uint32_t parameters[3];
+    const int eventCode = device->reapEventRequest(seq, &parameters);
     if (eventCode <= 0) {
         env->ThrowNew(clazz_operation_canceled_exception, "");
         return NULL;
     }
     jobject result = env->NewObject(clazz_event, constructor_event);
     env->SetIntField(result, field_event_eventCode, eventCode);
+    env->SetIntField(result, field_event_parameter1, static_cast<jint>(parameters[0]));
+    env->SetIntField(result, field_event_parameter2, static_cast<jint>(parameters[1]));
+    env->SetIntField(result, field_event_parameter3, static_cast<jint>(parameters[2]));
     return result;
 }
 
@@ -832,6 +839,21 @@
         ALOGE("Can't find MtpObjectInfo.mEventCode");
         return -1;
     }
+    field_event_parameter1 = env->GetFieldID(clazz, "mParameter1", "I");
+    if (field_event_parameter1 == NULL) {
+        ALOGE("Can't find MtpObjectInfo.mParameter1");
+        return -1;
+    }
+    field_event_parameter2 = env->GetFieldID(clazz, "mParameter2", "I");
+    if (field_event_parameter2 == NULL) {
+        ALOGE("Can't find MtpObjectInfo.mParameter2");
+        return -1;
+    }
+    field_event_parameter3 = env->GetFieldID(clazz, "mParameter3", "I");
+    if (field_event_parameter3 == NULL) {
+        ALOGE("Can't find MtpObjectInfo.mParameter3");
+        return -1;
+    }
     clazz_event = (jclass)env->NewGlobalRef(clazz);
 
     clazz = env->FindClass("android/mtp/MtpDevice");
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index abb464e..6fb8b51 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -56,7 +56,6 @@
 
 public class CaptivePortalLoginActivity extends Activity {
     private static final String TAG = "CaptivePortalLogin";
-    private static final String DEFAULT_SERVER = "connectivitycheck.gstatic.com";
     private static final int SOCKET_TIMEOUT_MS = 10000;
 
     private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS };
@@ -72,16 +71,14 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
-        String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
-        if (server == null) server = DEFAULT_SERVER;
         mCm = ConnectivityManager.from(this);
         String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
+        if (url == null) url = mCm.getCaptivePortalServerUrl();
         try {
-            mURL = url != null ? new URL(url) : new URL("http", server, "/generate_204");
+            mURL = new URL(url);
         } catch (MalformedURLException e) {
             // System misconfigured, bail out in a way that at least provides network access.
-            Log.e(TAG, "Invalid captive portal URL, server=" + server);
+            Log.e(TAG, "Invalid captive portal URL, url=" + url);
             done(Result.WANTED_AS_IS);
         }
         mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index f9bbccb..1b5911d 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -23,11 +23,11 @@
     <ProgressBar
         android:id="@+id/progressbar"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="@dimen/progress_bar_height"
         android:indeterminate="true"
         style="@style/TrimmedHorizontalProgressBar"
         android:visibility="gone"/>
-  
+
     <FrameLayout
         android:id="@+id/container_message_bar"
         android:layout_width="match_parent"
@@ -44,7 +44,7 @@
         android:layout_height="match_parent"
         android:orientation="vertical"
         android:visibility="gone">
-        
+
         <TextView
             android:id="@+id/message"
             android:layout_width="wrap_content"
@@ -58,9 +58,9 @@
             android:layout_height="wrap_content"
             android:text="@string/button_retry"
             style="?android:attr/buttonBarPositiveButtonStyle" />
-        
+
     </LinearLayout>
-    
+
     <!-- This FrameLayout works around b/24189541 -->
     <FrameLayout
         android:layout_width="match_parent"
@@ -68,6 +68,7 @@
 
         <android.support.v7.widget.RecyclerView
             android:id="@+id/list"
+            android:background="@color/window_background"
             android:scrollbars="vertical"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml
index cacdf4d..5adb165 100644
--- a/packages/DocumentsUI/res/values/dimens.xml
+++ b/packages/DocumentsUI/res/values/dimens.xml
@@ -15,6 +15,9 @@
 -->
 
 <resources>
+    <dimen name="grid_container_padding">10dp</dimen>
+    <dimen name="list_container_padding">0dp</dimen>
+
     <dimen name="icon_size">40dp</dimen>
     <dimen name="root_icon_size">24dp</dimen>
     <dimen name="root_icon_margin">0dp</dimen>
@@ -23,6 +26,8 @@
     <dimen name="list_item_thumbnail_size">40dp</dimen>
     <dimen name="grid_item_icon_size">30dp</dimen>
 
+    <dimen name="progress_bar_height">4dp</dimen>
+
     <dimen name="grid_width">152dp</dimen>
     <dimen name="grid_height">176dp</dimen>
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 898713f..22e81c6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -601,11 +601,11 @@
                 throw new IllegalArgumentException("Unsupported layout mode: " + mode);
         }
 
-        mRecView.setLayoutManager(layout);
-        // TODO: Once b/23691541 is resolved, use a listener within MultiSelectManager instead of
-        // imperatively calling this function.
-        mSelectionManager.handleLayoutChanged();
+        int pad = getDirectoryPadding(mode);
+        mRecView.setPadding(pad, pad, pad, pad);
         // setting layout manager automatically invalidates existing ViewHolders.
+        mRecView.setLayoutManager(layout);
+        mSelectionManager.handleLayoutChanged();  // RecyclerView doesn't do this for us
         mIconHelper.setMode(mode);
     }
 
@@ -621,6 +621,20 @@
         return columnCount;
     }
 
+    private int getDirectoryPadding(int mode) {
+        switch (mode) {
+            case MODE_GRID:
+                return getResources().getDimensionPixelSize(
+                        R.dimen.grid_container_padding);
+            case MODE_LIST:
+                return getResources().getDimensionPixelSize(
+                        R.dimen.list_container_padding);
+            case MODE_UNKNOWN:
+            default:
+                throw new IllegalArgumentException("Unsupported layout mode: " + mode);
+        }
+    }
+
     @Override
     public int getColumnCount() {
         return mColumnCount;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/EmptyDocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/EmptyDocumentHolder.java
deleted file mode 100644
index d1f8ff7..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/EmptyDocumentHolder.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2015 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.documentsui.dirlist;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.view.View;
-import android.widget.Space;
-
-import com.android.documentsui.R;
-import com.android.documentsui.State;
-
-final class EmptyDocumentHolder extends DocumentHolder {
-    final int mVisibleHeight;
-
-    public EmptyDocumentHolder(Context context) {
-        super(context, new Space(context));
-
-        // Per UX spec, this puts a bigger gap between the folders and documents in the grid.
-        mVisibleHeight = context.getResources().getDimensionPixelSize(R.dimen.grid_item_margin) * 2;
-    }
-
-    public void bind(State state) {
-        bind(null, null, state);
-    }
-
-    @Override
-    public void bind(Cursor cursor, String modelId, State state) {
-        if (state.derivedMode == State.MODE_GRID) {
-            itemView.setMinimumHeight(mVisibleHeight);
-        } else {
-            itemView.setMinimumHeight(0);
-        }
-        return;
-    }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java
index 3ee1d42..2485ad9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java
@@ -18,10 +18,16 @@
 
 import static com.android.internal.util.Preconditions.checkArgument;
 
+import android.content.Context;
+import android.database.Cursor;
 import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.RecyclerView.AdapterDataObserver;
 import android.util.SparseArray;
 import android.view.ViewGroup;
+import android.widget.Space;
+
+import com.android.documentsui.R;
+import com.android.documentsui.State;
 
 import java.util.List;
 
@@ -222,4 +228,33 @@
             throw new UnsupportedOperationException();
         }
     }
+
+    /**
+     * The most elegant transparent blank box that spans N rows ever conceived.
+     */
+    private static final class EmptyDocumentHolder extends DocumentHolder {
+        final int mVisibleHeight;
+
+        public EmptyDocumentHolder(Context context) {
+            super(context, new Space(context));
+
+            // Per UX spec, this puts a bigger gap between the folders and documents in the grid.
+            mVisibleHeight = context.getResources().getDimensionPixelSize(
+                    R.dimen.grid_item_margin);
+        }
+
+        public void bind(State state) {
+            bind(null, null, state);
+        }
+
+        @Override
+        public void bind(Cursor cursor, String modelId, State state) {
+            if (state.derivedMode == State.MODE_GRID) {
+                itemView.setMinimumHeight(mVisibleHeight);
+            } else {
+                itemView.setMinimumHeight(0);
+            }
+            return;
+        }
+    }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index c216c77..15b8ef3 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -7,9 +7,6 @@
 import android.provider.DocumentsContract;
 import android.util.Log;
 
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.FutureTask;
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
index 49b48c5..7527f54 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
@@ -19,6 +19,8 @@
 import android.content.Context;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
+import android.mtp.MtpConstants;
+import android.mtp.MtpEvent;
 import android.os.CancellationSignal;
 import android.os.OperationCanceledException;
 import android.os.SystemClock;
@@ -32,12 +34,10 @@
 
 @RealDeviceTest
 public class MtpManagerTest extends InstrumentationTestCase {
-
     private static final int TIMEOUT_MS = 1000;
     UsbManager mUsbManager;
     MtpManager mManager;
     UsbDevice mUsbDevice;
-    int mRequest;
 
     @Override
     public void setUp() throws Exception {
@@ -85,6 +85,19 @@
         getInstrumentation().show(Arrays.toString(records[0].operationsSupported));
     }
 
+    public void testEventObjectAdded() throws Exception {
+        while (true) {
+            getInstrumentation().show("Please take a photo by using connected MTP device.");
+            final CancellationSignal signal = new CancellationSignal();
+            MtpEvent event = mManager.readEvent(mUsbDevice.getDeviceId(), signal);
+            if (event.getEventCode() != MtpConstants.EVENT_OBJECT_ADDED) {
+                continue;
+            }
+            assertTrue(event.getObjectHandle() != 0);
+            break;
+        }
+    }
+
     private Context getContext() {
         return getInstrumentation().getContext();
     }
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index c860668..2189b55 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -3,9 +3,22 @@
 
 LOCAL_MODULE := SettingsLib
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-v4 \
+    android-support-v7-recyclerview \
+    android-support-v7-preference \
+    android-support-v7-appcompat \
+    android-support-v14-preference
 
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
+    frameworks/support/v7/preference/res \
+    frameworks/support/v14/preference/res \
+    frameworks/support/v7/appcompat/res \
+    frameworks/support/v7/recyclerview/res
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_AAPT_FLAGS := --auto-add-overlay \
+    --extra-packages android.support.v7.preference:android.support.v14.preference:android.support.v17.preference:android.support.v7.appcompat:android.support.v7.recyclerview
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml b/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml
new file mode 100644
index 0000000..b3d7cf9
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="21dp"
+        android:height="21dp"
+        android:viewportWidth="21.0"
+        android:viewportHeight="21.0"
+        android:tint="?android:attr/colorAccent">
+    <path
+            android:fillColor="@android:color/white"
+            android:pathData="M8,16c1.1,0,2-0.9,2-2s-0.9-2-2-2s-2,0.9-2,2S6.9,16,8,16zM14,7h-1V5c0-2.8-2.2-5-5-5S3,2.2,3,5v2H2C0.9,7,0,7.9,0,9v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V9C16,7.9,15.1,7,14,7z M4.9,5c0-1.7,1.4-3.1,3.1-3.1s3.1,1.4,3.1,3.1v2H4.9V5z M14,19H2V9h12V19z" />
+</vector>
diff --git a/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml b/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml
new file mode 100644
index 0000000..f7a9c9f
--- /dev/null
+++ b/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@android:id/text1"
+        style="?android:attr/spinnerDropDownItemStyle"
+        android:singleLine="true"
+        android:layout_width="wrap_content"
+        android:layout_height="?android:attr/listPreferredItemHeightSmall"
+        android:ellipsize="marquee" />
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml
new file mode 100644
index 0000000..46267a2
--- /dev/null
+++ b/packages/SettingsLib/res/values/attrs.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<resources>
+    <declare-styleable name="RestrictedPreference">
+        <attr name="userRestriction" format="string"/>
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
new file mode 100644
index 0000000..c090468
--- /dev/null
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<resources>
+    <color name="disabled_text_color">#66000000</color> <!-- 38% black -->
+</resources>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index d7c78f6..9a1d6a4 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -31,4 +31,8 @@
     <dimen name="user_spinner_padding">4dp</dimen>
     <dimen name="user_spinner_padding_sides">20dp</dimen>
     <dimen name="user_spinner_item_height">56dp</dimen>
+
+    <!-- Lock icon for preferences locked by admin -->
+    <dimen name="restricted_lock_icon_size">16dp</dimen>
+    <dimen name="restricted_lock_icon_padding">4dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ac19cf5..6dfa9ad 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -684,6 +684,8 @@
     <string name="select_webview_provider_title">WebView implementation</string>
     <!-- Developer settings: select WebView provider dialog title -->
     <string name="select_webview_provider_dialog_title">Set WebView implementation</string>
+    <!-- Developer settings: confirmation dialog text for the WebView provider selection dialog -->
+    <string name="select_webview_provider_confirmation_text">The chosen WebView implementation is disabled, and must be enabled to be used, do you wish to enable it?</string>
 
     <!-- Developer settings screen, convert userdata to file encryption option name -->
     <string name="convert_to_file_encryption">Convert to file encryption</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
new file mode 100644
index 0000000..c2f885d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.v7.preference.DropDownPreference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+public class RestrictedDropDownPreference extends DropDownPreference {
+    private Spinner mSpinner;
+    private final Drawable mRestrictedPadlock;
+    private final int mRestrictedPadlockPadding;
+    private List<RestrictedItem> mRestrictedItems = new ArrayList<>();
+
+    public RestrictedDropDownPreference(Context context) {
+        this(context, null);
+    }
+
+    public RestrictedDropDownPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(context);
+        mRestrictedPadlockPadding = context.getResources().getDimensionPixelSize(
+                R.dimen.restricted_lock_icon_padding);
+    }
+
+    private final OnItemSelectedListener mItemSelectedListener = new OnItemSelectedListener() {
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
+            if (position >= 0) {
+                String value = getEntryValues()[position].toString();
+                RestrictedItem item = getRestrictedItemForEntryValue(value);
+                if (item != null) {
+                    RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
+                            item.enforcedAdmin);
+                    mSpinner.setSelection(findIndexOfValue(getValue()));
+                } else if (!value.equals(getValue()) && callChangeListener(value)) {
+                    setValue(value);
+                }
+            }
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // noop
+        }
+    };
+
+    @Override
+    protected ArrayAdapter createAdapter() {
+        return new RestrictedArrayItemAdapter(getContext());
+    }
+
+    @Override
+    public void setValue(String value) {
+        if (getRestrictedItemForEntryValue(value) != null) {
+            return;
+        }
+        super.setValue(value);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder view) {
+        super.onBindViewHolder(view);
+        mSpinner = (Spinner) view.itemView.findViewById(R.id.spinner);
+        mSpinner.setOnItemSelectedListener(mItemSelectedListener);
+    }
+
+    private class RestrictedArrayItemAdapter extends ArrayAdapter<String> {
+        public RestrictedArrayItemAdapter(Context context) {
+            super(context, R.layout.spinner_dropdown_restricted_item);
+        }
+
+        @Override
+        public View getDropDownView(int position, View convertView, ViewGroup parent) {
+            TextView view = (TextView) super.getView(position, convertView, parent);
+            CharSequence entry = getItem(position);
+            boolean isEntryRestricted = isRestrictedForEntry(entry);
+            RestrictedLockUtils.setTextViewPadlock(getContext(), view, isEntryRestricted);
+            view.setEnabled(!isEntryRestricted);
+            return view;
+        }
+    }
+
+    private boolean isRestrictedForEntry(CharSequence entry) {
+        if (entry == null) {
+            return false;
+        }
+        for (RestrictedItem item : mRestrictedItems) {
+            if (entry.equals(item.entry)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private RestrictedItem getRestrictedItemForEntryValue(CharSequence entryValue) {
+        if (entryValue == null) {
+            return null;
+        }
+        for (RestrictedItem item : mRestrictedItems) {
+            if (entryValue.equals(item.entryValue)) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    public void addRestrictedItem(RestrictedItem item) {
+        mRestrictedItems.add(item);
+    }
+
+    public static class RestrictedItem {
+        public CharSequence entry;
+        public CharSequence entryValue;
+        public EnforcedAdmin enforcedAdmin;
+
+        public RestrictedItem(CharSequence entry, CharSequence entryValue,
+                EnforcedAdmin enforcedAdmin) {
+            this.entry = entry;
+            this.entryValue = entryValue;
+            this.enforcedAdmin = enforcedAdmin;
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java
new file mode 100644
index 0000000..e63130d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.text.style.ImageSpan;
+
+/**
+ * An extension of ImageSpan which adds a padding before the image.
+ */
+public class RestrictedLockImageSpan extends ImageSpan {
+    private Context mContext;
+    private final float mExtraPadding;
+    private final Drawable mRestrictedPadlock;
+
+    public RestrictedLockImageSpan(Context context) {
+        // we are overriding getDrawable, so passing null to super class here.
+        super((Drawable) null);
+
+        mContext = context;
+        mExtraPadding = mContext.getResources().getDimensionPixelSize(
+                R.dimen.restricted_lock_icon_padding);
+        mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext);
+    }
+
+    @Override
+    public Drawable getDrawable() {
+        return mRestrictedPadlock;
+    }
+
+    @Override
+    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y,
+            int bottom, Paint paint) {
+        Drawable drawable = getDrawable();
+        canvas.save();
+
+        // Add extra padding before the padlock.
+        float transX = x + mExtraPadding;
+        float transY = bottom - drawable.getBounds().bottom - paint.getFontMetricsInt().descent;
+
+        canvas.translate(transX, transY);
+        drawable.draw(canvas);
+        canvas.restore();
+    }
+
+    @Override
+    public int getSize(Paint paint, CharSequence text, int start, int end,
+            Paint.FontMetricsInt fontMetrics) {
+        int size = super.getSize(paint, text, start, end, fontMetrics);
+        size += 2 * mExtraPadding;
+        return size;
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
new file mode 100644
index 0000000..f6caaa9
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.Spanned;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.ImageSpan;
+import android.view.MenuItem;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * Utility class to host methods usable in adding a restricted padlock icon and showing admin
+ * support message dialog.
+ */
+public class RestrictedLockUtils {
+    /**
+     * @return drawables for displaying with settings that are locked by a device admin.
+     */
+    public static Drawable getRestrictedPadlock(Context context) {
+        Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_settings_lock_outline);
+        final int iconSize = context.getResources().getDimensionPixelSize(
+                R.dimen.restricted_lock_icon_size);
+        restrictedPadlock.setBounds(0, 0, iconSize, iconSize);
+        return restrictedPadlock;
+    }
+
+    /**
+     * Checks if a restriction is enforced on a user and returns the enforced admin and
+     * admin userId.
+     *
+     * @param userRestriction Restriction to check
+     * @param userId User which we need to check if restriction is enforced on.
+     * @return EnforcedAdmin Object containing the enforce admin and admin user details, or
+     * {@code null} If the restriction is not set. If the restriction is set by both device owner
+     * and profile owner, then the admin will be set to {@code null} and userId to
+     * {@link UserHandle#USER_NULL}.
+     */
+    public static EnforcedAdmin checkIfRestrictionEnforced(Context context,
+            String userRestriction, int userId) {
+        DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
+        int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
+        boolean enforcedByDeviceOwner = false;
+        if (deviceOwner != null && deviceOwnerUserId != UserHandle.USER_NULL) {
+            Bundle enforcedRestrictions = dpm.getUserRestrictions(deviceOwner, deviceOwnerUserId);
+            if (enforcedRestrictions != null
+                    && enforcedRestrictions.getBoolean(userRestriction, false)) {
+                enforcedByDeviceOwner = true;
+            }
+        }
+
+        ComponentName profileOwner = null;
+        boolean enforcedByProfileOwner = false;
+        if (userId != UserHandle.USER_NULL) {
+            profileOwner = dpm.getProfileOwnerAsUser(userId);
+            if (profileOwner != null) {
+                Bundle enforcedRestrictions = dpm.getUserRestrictions(profileOwner, userId);
+                if (enforcedRestrictions != null
+                        && enforcedRestrictions.getBoolean(userRestriction, false)) {
+                    enforcedByProfileOwner = true;
+                }
+            }
+        }
+
+        if (!enforcedByDeviceOwner && !enforcedByProfileOwner) {
+            return null;
+        }
+
+        EnforcedAdmin admin = null;
+        if (enforcedByDeviceOwner && enforcedByProfileOwner) {
+            admin = new EnforcedAdmin();
+        } else if (enforcedByDeviceOwner) {
+            admin = new EnforcedAdmin(deviceOwner, deviceOwnerUserId);
+        } else {
+            admin = new EnforcedAdmin(profileOwner, userId);
+        }
+        return admin;
+    }
+
+    /**
+     * Checks if lock screen notification features are disabled by policy. This should be
+     * only used for keyguard notification features but not the keyguard features
+     * (e.g. KEYGUARD_DISABLE_FINGERPRINT) where a profile owner can set them on the parent user
+     * as it won't work for that case.
+     *
+     * @param keyguardNotificationFeatures Could be any of notification features that can be
+     * disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}.
+     * @return EnforcedAdmin Object containing the enforce admin and admin user details, or
+     * {@code null} If the notification features are not disabled. If the restriction is set by
+     * multiple admins, then the admin will be set to {@code null} and userId to
+     * {@link UserHandle#USER_NULL}.
+     */
+    public static EnforcedAdmin checkIfKeyguardNotificationFeaturesDisabled(Context context,
+            int keyguardNotificationFeatures) {
+        final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        boolean isDisabledByMultipleAdmins = false;
+        ComponentName adminComponent = null;
+        List<ComponentName> admins = dpm.getActiveAdmins();
+        int disabledKeyguardFeatures;
+        for (ComponentName admin : admins) {
+            disabledKeyguardFeatures = dpm.getKeyguardDisabledFeatures(admin);
+            if ((disabledKeyguardFeatures & keyguardNotificationFeatures) != 0) {
+                if (adminComponent == null) {
+                    adminComponent = admin;
+                } else {
+                    isDisabledByMultipleAdmins = true;
+                    break;
+                }
+            }
+        }
+        EnforcedAdmin enforcedAdmin = null;
+        if (adminComponent != null) {
+            if (!isDisabledByMultipleAdmins) {
+                enforcedAdmin = new EnforcedAdmin(adminComponent, UserHandle.myUserId());
+            } else {
+                enforcedAdmin = new EnforcedAdmin();
+            }
+        }
+        return enforcedAdmin;
+    }
+
+    /**
+     * Set the menu item as disabled by admin by adding a restricted padlock at the end of the
+     * text and set the click listener which will send an intent to show the admin support details
+     * dialog.
+     */
+    public static void setMenuItemAsDisabledByAdmin(final Context context,
+            final MenuItem item, final EnforcedAdmin admin) {
+        SpannableStringBuilder sb = new SpannableStringBuilder(item.getTitle());
+        removeExistingRestrictedSpans(sb);
+
+        final int disabledColor = context.getColor(R.color.disabled_text_color);
+        sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(),
+                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        ImageSpan image = new RestrictedLockImageSpan(context);
+        sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        item.setTitle(sb);
+
+        item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+            @Override
+            public boolean onMenuItemClick(MenuItem item) {
+                sendShowAdminSupportDetailsIntent(context, admin);
+                return true;
+            }
+        });
+    }
+
+    private static void removeExistingRestrictedSpans(SpannableStringBuilder sb) {
+        final int length = sb.length();
+        RestrictedLockImageSpan[] imageSpans = sb.getSpans(length - 1, length,
+                RestrictedLockImageSpan.class);
+        for (ImageSpan span : imageSpans) {
+            sb.removeSpan(span);
+        }
+        ForegroundColorSpan[] colorSpans = sb.getSpans(0, length, ForegroundColorSpan.class);
+        for (ForegroundColorSpan span : colorSpans) {
+            sb.removeSpan(span);
+        }
+    }
+
+    /**
+     * Send the intent to trigger the {@link android.settings.ShowAdminSupportDetailsDialog}.
+     */
+    public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
+        Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
+        int adminUserId = UserHandle.myUserId();
+        if (admin != null) {
+            if (admin.component != null) {
+                intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component);
+            }
+            if (admin.userId != UserHandle.USER_NULL) {
+                adminUserId = admin.userId;
+            }
+            intent.putExtra(Intent.EXTRA_USER_ID, adminUserId);
+        }
+        context.startActivityAsUser(intent, new UserHandle(adminUserId));
+    }
+
+    public static void setTextViewPadlock(Context context,
+            TextView textView, boolean showPadlock) {
+        final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText());
+        removeExistingRestrictedSpans(sb);
+        if (showPadlock) {
+            final ImageSpan image = new RestrictedLockImageSpan(context);
+            sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        }
+        textView.setText(sb);
+    }
+
+    public static class EnforcedAdmin {
+        public ComponentName component = null;
+        public int userId = UserHandle.USER_NULL;
+
+        public EnforcedAdmin(ComponentName component, int userId) {
+            this.component = component;
+            this.userId = userId;
+        }
+
+        public EnforcedAdmin() {}
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
new file mode 100644
index 0000000..569017a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Preference class that supports being disabled by a user restriction
+ * set by a device admin.
+ */
+public class RestrictedPreference extends Preference {
+    RestrictedPreferenceHelper mHelper;
+
+    public RestrictedPreference(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mHelper = new RestrictedPreferenceHelper(context, this, attrs);
+    }
+
+    public RestrictedPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public RestrictedPreference(Context context, AttributeSet attrs) {
+        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
+                android.R.attr.preferenceStyle));
+    }
+
+    public RestrictedPreference(Context context) {
+        this(context, null);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        mHelper.onBindViewHolder(holder);
+    }
+
+    @Override
+    public void performClick() {
+        if (!mHelper.performClick()) {
+            super.performClick();
+        }
+    }
+
+    @Override
+    protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
+        mHelper.onAttachedToHierarchy();
+        super.onAttachedToHierarchy(preferenceManager);
+    }
+
+    public void checkRestrictionAndSetDisabled(String userRestriction) {
+        mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
+    }
+
+    public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
+        mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
+    }
+
+    public void setDisabledByAdmin(EnforcedAdmin admin) {
+        if (mHelper.setDisabledByAdmin(admin)) {
+            notifyChanged();
+        }
+    }
+
+    public boolean isDisabledByAdmin() {
+        return mHelper.isDisabledByAdmin();
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
new file mode 100644
index 0000000..f041504
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.Spanned;
+import android.text.SpannableStringBuilder;
+import android.text.style.ImageSpan;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Helper class for managing settings preferences that can be disabled
+ * by device admins via user restrictions.
+ */
+public class RestrictedPreferenceHelper {
+    private final Context mContext;
+    private final Preference mPreference;
+    private final Drawable mRestrictedPadlock;
+    private final int mRestrictedPadlockPadding;
+
+    private boolean mDisabledByAdmin;
+    private EnforcedAdmin mEnforcedAdmin;
+    private String mAttrUserRestriction = null;
+
+    RestrictedPreferenceHelper(Context context, Preference preference,
+            AttributeSet attrs) {
+        mContext = context;
+        mPreference = preference;
+
+        mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext);
+        mRestrictedPadlockPadding = mContext.getResources().getDimensionPixelSize(
+                R.dimen.restricted_lock_icon_padding);
+
+        mAttrUserRestriction = attrs.getAttributeValue(
+                R.styleable.RestrictedPreference_userRestriction);
+        final TypedArray attributes = context.obtainStyledAttributes(attrs,
+                R.styleable.RestrictedPreference);
+        final TypedValue userRestriction =
+                attributes.peekValue(R.styleable.RestrictedPreference_userRestriction);
+        CharSequence data = null;
+        if (userRestriction != null && userRestriction.type == TypedValue.TYPE_STRING) {
+            if (userRestriction.resourceId != 0) {
+                data = context.getText(userRestriction.resourceId);
+            } else {
+                data = userRestriction.string;
+            }
+        }
+        mAttrUserRestriction = data == null ? null : data.toString();
+    }
+
+    /**
+     * Modify PreferenceViewHolder to add padlock if restriction is disabled.
+     */
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        final TextView titleView = (TextView) holder.findViewById(android.R.id.title);
+        if (titleView != null) {
+            RestrictedLockUtils.setTextViewPadlock(mContext, titleView, mDisabledByAdmin);
+            if (mDisabledByAdmin) {
+                holder.itemView.setEnabled(true);
+            }
+        }
+    }
+
+    /**
+     * Check if the preference is disabled if so handle the click by informing the user.
+     *
+     * @return true if the method handled the click.
+     */
+    public boolean performClick() {
+        if (mDisabledByAdmin) {
+            RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Disable / enable if we have been passed the restriction in the xml.
+     */
+    protected void onAttachedToHierarchy() {
+        if (mAttrUserRestriction != null) {
+            checkRestrictionAndSetDisabled(mAttrUserRestriction, UserHandle.myUserId());
+        }
+    }
+
+    /**
+     * Set the user restriction that is used to disable this preference.
+     *
+     * @param userRestriction constant from {@link android.os.UserManager}
+     * @param userId user to check the restriction for.
+     */
+    public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
+        EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
+                userRestriction, userId);
+        setDisabledByAdmin(admin);
+    }
+
+    /**
+     * Disable this preference based on the enforce admin.
+     *
+     * @param EnforcedAdmin Details of the admin who enforced the restriction. If it
+     * is {@code null}, then this preference will be enabled. Otherwise, it will be disabled.
+     * @return true if the disabled state was changed.
+     */
+    public boolean setDisabledByAdmin(EnforcedAdmin admin) {
+        final boolean disabled = (admin != null ? true : false);
+        mEnforcedAdmin = (disabled ? admin : null);
+        if (mDisabledByAdmin != disabled) {
+            mDisabledByAdmin = disabled;
+            mPreference.setEnabled(!disabled);
+            return true;
+        }
+        return false;
+    }
+
+    public boolean isDisabledByAdmin() {
+        return mDisabledByAdmin;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
new file mode 100644
index 0000000..308477b0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.support.v14.preference.SwitchPreference;
+import android.util.AttributeSet;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Version of SwitchPreference that can be disabled by a device admin
+ * using a user restriction.
+ */
+public class RestrictedSwitchPreference extends SwitchPreference {
+    RestrictedPreferenceHelper mHelper;
+
+    public RestrictedSwitchPreference(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mHelper = new RestrictedPreferenceHelper(context, this, attrs);
+    }
+
+    public RestrictedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public RestrictedSwitchPreference(Context context, AttributeSet attrs) {
+        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.switchPreferenceStyle,
+                android.R.attr.switchPreferenceStyle));
+    }
+
+    public RestrictedSwitchPreference(Context context) {
+        this(context, null);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        mHelper.onBindViewHolder(holder);
+    }
+
+    @Override
+    public void performClick() {
+        if (!mHelper.performClick()) {
+            super.performClick();
+        }
+    }
+
+    @Override
+    protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
+        mHelper.onAttachedToHierarchy();
+        super.onAttachedToHierarchy(preferenceManager);
+    }
+
+    public void checkRestrictionAndSetDisabled(String userRestriction) {
+        mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
+    }
+
+    public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
+        mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
+    }
+
+    public void setDisabledByAdmin(EnforcedAdmin admin) {
+        if (mHelper.setDisabledByAdmin(admin)) {
+            notifyChanged();
+        }
+    }
+
+    public boolean isDisabledByAdmin() {
+        return mHelper.isDisabledByAdmin();
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
new file mode 100644
index 0000000..f1beb10
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib.users;
+
+import android.app.AppGlobals;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class AppRestrictionsHelper {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "AppRestrictionsHelper";
+
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    private final IPackageManager mIPm;
+    private final UserManager mUserManager;
+    private final UserHandle mUser;
+    private final boolean mRestrictedProfile;
+
+    HashMap<String,Boolean> mSelectedPackages = new HashMap<>();
+    private List<SelectableAppInfo> mVisibleApps;
+
+    public AppRestrictionsHelper(Context context, UserHandle user) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+        mIPm = AppGlobals.getPackageManager();
+        mUser = user;
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        mRestrictedProfile = mUserManager.getUserInfo(mUser.getIdentifier()).isRestricted();
+    }
+
+    public void setPackageSelected(String packageName, boolean selected) {
+        mSelectedPackages.put(packageName, selected);
+    }
+
+    public boolean isPackageSelected(String packageName) {
+        return mSelectedPackages.get(packageName);
+    }
+
+    public List<SelectableAppInfo> getVisibleApps() {
+        return mVisibleApps;
+    }
+
+    public void applyUserAppsStates(OnDisableUiForPackageListener listener) {
+        final int userId = mUser.getIdentifier();
+        if (!mUserManager.getUserInfo(userId).isRestricted() && userId != UserHandle.myUserId()) {
+            Log.e(TAG, "Cannot apply application restrictions on another user!");
+            return;
+        }
+        for (Map.Entry<String,Boolean> entry : mSelectedPackages.entrySet()) {
+            String packageName = entry.getKey();
+            boolean enabled = entry.getValue();
+            applyUserAppState(packageName, enabled, listener);
+        }
+    }
+
+    public void applyUserAppState(String packageName, boolean enabled,
+            OnDisableUiForPackageListener listener) {
+        final int userId = mUser.getIdentifier();
+        if (enabled) {
+            // Enable selected apps
+            try {
+                ApplicationInfo info = mIPm.getApplicationInfo(packageName,
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+                if (info == null || !info.enabled
+                        || (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+                    mIPm.installExistingPackageAsUser(packageName, mUser.getIdentifier());
+                    if (DEBUG) {
+                        Log.d(TAG, "Installing " + packageName);
+                    }
+                }
+                if (info != null && (info.privateFlags&ApplicationInfo.PRIVATE_FLAG_HIDDEN) != 0
+                        && (info.flags&ApplicationInfo.FLAG_INSTALLED) != 0) {
+                    listener.onDisableUiForPackage(packageName);
+                    mIPm.setApplicationHiddenSettingAsUser(packageName, false, userId);
+                    if (DEBUG) {
+                        Log.d(TAG, "Unhiding " + packageName);
+                    }
+                }
+            } catch (RemoteException re) {
+                // Ignore
+            }
+        } else {
+            // Blacklist all other apps, system or downloaded
+            try {
+                ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId);
+                if (info != null) {
+                    if (mRestrictedProfile) {
+                        mIPm.deletePackageAsUser(packageName, null, mUser.getIdentifier(),
+                                PackageManager.DELETE_SYSTEM_APP);
+                        if (DEBUG) {
+                            Log.d(TAG, "Uninstalling " + packageName);
+                        }
+                    } else {
+                        listener.onDisableUiForPackage(packageName);
+                        mIPm.setApplicationHiddenSettingAsUser(packageName, true, userId);
+                        if (DEBUG) {
+                            Log.d(TAG, "Hiding " + packageName);
+                        }
+                    }
+                }
+            } catch (RemoteException re) {
+                // Ignore
+            }
+        }
+    }
+
+    public void fetchAndMergeApps() {
+        mVisibleApps = new ArrayList<>();
+        final PackageManager pm = mPackageManager;
+        final IPackageManager ipm = mIPm;
+
+        final HashSet<String> excludePackages = new HashSet<>();
+        addSystemImes(excludePackages);
+
+        // Add launchers
+        Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
+        launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        addSystemApps(mVisibleApps, launcherIntent, excludePackages);
+
+        // Add widgets
+        Intent widgetIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+        addSystemApps(mVisibleApps, widgetIntent, excludePackages);
+
+        List<ApplicationInfo> installedApps = pm.getInstalledApplications(
+                PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        for (ApplicationInfo app : installedApps) {
+            // If it's not installed, skip
+            if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue;
+
+            if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+                    && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
+                // Downloaded app
+                SelectableAppInfo info = new SelectableAppInfo();
+                info.packageName = app.packageName;
+                info.appName = app.loadLabel(pm);
+                info.activityName = info.appName;
+                info.icon = app.loadIcon(pm);
+                mVisibleApps.add(info);
+            } else {
+                try {
+                    PackageInfo pi = pm.getPackageInfo(app.packageName, 0);
+                    // If it's a system app that requires an account and doesn't see restricted
+                    // accounts, mark for removal. It might get shown in the UI if it has an icon
+                    // but will still be marked as false and immutable.
+                    if (mRestrictedProfile
+                            && pi.requiredAccountType != null && pi.restrictedAccountType == null) {
+                        mSelectedPackages.put(app.packageName, false);
+                    }
+                } catch (PackageManager.NameNotFoundException re) {
+                    // Skip
+                }
+            }
+        }
+
+        // Get the list of apps already installed for the user
+        List<ApplicationInfo> userApps = null;
+        try {
+            ParceledListSlice<ApplicationInfo> listSlice = ipm.getInstalledApplications(
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES, mUser.getIdentifier());
+            if (listSlice != null) {
+                userApps = listSlice.getList();
+            }
+        } catch (RemoteException re) {
+            // Ignore
+        }
+
+        if (userApps != null) {
+            for (ApplicationInfo app : userApps) {
+                if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue;
+
+                if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+                        && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
+                    // Downloaded app
+                    SelectableAppInfo info = new SelectableAppInfo();
+                    info.packageName = app.packageName;
+                    info.appName = app.loadLabel(pm);
+                    info.activityName = info.appName;
+                    info.icon = app.loadIcon(pm);
+                    mVisibleApps.add(info);
+                }
+            }
+        }
+
+        // Sort the list of visible apps
+        Collections.sort(mVisibleApps, new AppLabelComparator());
+
+        // Remove dupes
+        Set<String> dedupPackageSet = new HashSet<String>();
+        for (int i = mVisibleApps.size() - 1; i >= 0; i--) {
+            SelectableAppInfo info = mVisibleApps.get(i);
+            if (DEBUG) Log.i(TAG, info.toString());
+            String both = info.packageName + "+" + info.activityName;
+            if (!TextUtils.isEmpty(info.packageName)
+                    && !TextUtils.isEmpty(info.activityName)
+                    && dedupPackageSet.contains(both)) {
+                mVisibleApps.remove(i);
+            } else {
+                dedupPackageSet.add(both);
+            }
+        }
+
+        // Establish master/slave relationship for entries that share a package name
+        HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>();
+        for (SelectableAppInfo info : mVisibleApps) {
+            if (packageMap.containsKey(info.packageName)) {
+                info.masterEntry = packageMap.get(info.packageName);
+            } else {
+                packageMap.put(info.packageName, info);
+            }
+        }
+    }
+
+    /**
+     * Find all pre-installed input methods that are marked as default
+     * and add them to an exclusion list so that they aren't
+     * presented to the user for toggling.
+     * Don't add non-default ones, as they may include other stuff that we
+     * don't need to auto-include.
+     * @param excludePackages the set of package names to append to
+     */
+    private void addSystemImes(Set<String> excludePackages) {
+        InputMethodManager imm = (InputMethodManager)
+                mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+        List<InputMethodInfo> imis = imm.getInputMethodList();
+        for (InputMethodInfo imi : imis) {
+            try {
+                if (imi.isDefault(mContext) && isSystemPackage(imi.getPackageName())) {
+                    excludePackages.add(imi.getPackageName());
+                }
+            } catch (Resources.NotFoundException rnfe) {
+                // Not default
+            }
+        }
+    }
+
+    /**
+     * Add system apps that match an intent to the list, excluding any packages in the exclude list.
+     * @param visibleApps list of apps to append the new list to
+     * @param intent the intent to match
+     * @param excludePackages the set of package names to be excluded, since they're required
+     */
+    private void addSystemApps(List<SelectableAppInfo> visibleApps, Intent intent,
+            Set<String> excludePackages) {
+        final PackageManager pm = mPackageManager;
+        List<ResolveInfo> launchableApps = pm.queryIntentActivities(intent,
+                PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        for (ResolveInfo app : launchableApps) {
+            if (app.activityInfo != null && app.activityInfo.applicationInfo != null) {
+                final String packageName = app.activityInfo.packageName;
+                int flags = app.activityInfo.applicationInfo.flags;
+                if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                        || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+                    // System app
+                    // Skip excluded packages
+                    if (excludePackages.contains(packageName)) continue;
+                    int enabled = pm.getApplicationEnabledSetting(packageName);
+                    if (enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+                            || enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
+                        // Check if the app is already enabled for the target user
+                        ApplicationInfo targetUserAppInfo = getAppInfoForUser(packageName,
+                                0, mUser);
+                        if (targetUserAppInfo == null
+                                || (targetUserAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+                            continue;
+                        }
+                    }
+                    SelectableAppInfo info = new SelectableAppInfo();
+                    info.packageName = app.activityInfo.packageName;
+                    info.appName = app.activityInfo.applicationInfo.loadLabel(pm);
+                    info.icon = app.activityInfo.loadIcon(pm);
+                    info.activityName = app.activityInfo.loadLabel(pm);
+                    if (info.activityName == null) info.activityName = info.appName;
+
+                    visibleApps.add(info);
+                }
+            }
+        }
+    }
+
+    private boolean isSystemPackage(String packageName) {
+        try {
+            final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
+            if (pi.applicationInfo == null) return false;
+            final int flags = pi.applicationInfo.flags;
+            if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                    || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+                return true;
+            }
+        } catch (PackageManager.NameNotFoundException nnfe) {
+            // Missing package?
+        }
+        return false;
+    }
+
+    private ApplicationInfo getAppInfoForUser(String packageName, int flags, UserHandle user) {
+        try {
+            return mIPm.getApplicationInfo(packageName, flags, user.getIdentifier());
+        } catch (RemoteException re) {
+            return null;
+        }
+    }
+
+    public interface OnDisableUiForPackageListener {
+        void onDisableUiForPackage(String packageName);
+    }
+
+    public static class SelectableAppInfo {
+        public String packageName;
+        public CharSequence appName;
+        public CharSequence activityName;
+        public Drawable icon;
+        public SelectableAppInfo masterEntry;
+
+        @Override
+        public String toString() {
+            return packageName + ": appName=" + appName + "; activityName=" + activityName
+                    + "; icon=" + icon + "; masterEntry=" + masterEntry;
+        }
+    }
+
+    private static class AppLabelComparator implements Comparator<SelectableAppInfo> {
+
+        @Override
+        public int compare(SelectableAppInfo lhs, SelectableAppInfo rhs) {
+            String lhsLabel = lhs.activityName.toString();
+            String rhsLabel = rhs.activityName.toString();
+            return lhsLabel.toLowerCase().compareTo(rhsLabel.toLowerCase());
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 51cae86..fdb0d32 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -289,7 +289,7 @@
                     //       never happen)
                     specs.add(composeOffscreenAnimationSpec(t, offscreenTaskRect));
                 } else {
-                    layoutAlgorithm.getStackTransform(task, stackScroll, mTmpTransform, null);
+                    layoutAlgorithm.getStackTransform(t, stackScroll, mTmpTransform, null);
                     specs.add(composeAnimationSpec(tv, mTmpTransform, true /* addHeaderBitmap */));
                 }
             }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index 28121b4..e310801 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -64,6 +64,14 @@
     // Indicates that motion events are being collected to match a gesture.
     private boolean mRecognizingGesture;
 
+    // Indicates that motion events from the second pointer are being checked
+    // for a double tap.
+    private boolean mSecondFingerDoubleTap;
+
+    // Tracks the most recent time where ACTION_POINTER_DOWN was sent for the
+    // second pointer.
+    private long mSecondPointerDownTime;
+
     // Policy flags of the previous event.
     private int mPolicyFlags;
 
@@ -102,6 +110,7 @@
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 mDoubleTapDetected = false;
+                mSecondFingerDoubleTap = false;
                 mRecognizingGesture = true;
                 mPreviousX = x;
                 mPreviousY = y;
@@ -133,6 +142,43 @@
                     }
                 }
                 break;
+
+            case MotionEvent.ACTION_POINTER_DOWN:
+                // Once a second finger is used, we're definitely not
+                // recognizing a gesture.
+                cancelGesture();
+
+                if (event.getPointerCount() == 2) {
+                    // If this was the second finger, attempt to recognize double
+                    // taps on it.
+                    mSecondFingerDoubleTap = true;
+                    mSecondPointerDownTime = event.getEventTime();
+                } else {
+                    // If there are more than two fingers down, stop watching
+                    // for a double tap.
+                    mSecondFingerDoubleTap = false;
+                }
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                // If we're detecting taps on the second finger, see if we
+                // should finish the double tap.
+                if (mSecondFingerDoubleTap && maybeFinishDoubleTap(event, policyFlags)) {
+                    return true;
+                }
+                break;
+        }
+
+        // If we're detecting taps on the second finger, map events from the
+        // finger to the first finger.
+        if (mSecondFingerDoubleTap) {
+            MotionEvent newEvent = mapSecondPointerToFirstPointer(event);
+            if (newEvent == null) {
+                return false;
+            }
+            boolean handled = mGestureDetector.onTouchEvent(newEvent);
+            newEvent.recycle();
+            return handled;
         }
 
         if (!mRecognizingGesture) {
@@ -146,6 +192,7 @@
     public void clear() {
         mFirstTapDetected = false;
         mDoubleTapDetected = false;
+        mSecondFingerDoubleTap = false;
         cancelGesture();
         mStrokeBuffer.clear();
     }
@@ -229,4 +276,28 @@
 
         return false;
     }
+
+    private MotionEvent mapSecondPointerToFirstPointer(MotionEvent event) {
+        // Only map basic events when two fingers are down.
+        if (event.getPointerCount() != 2 ||
+                (event.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN &&
+                 event.getActionMasked() != MotionEvent.ACTION_POINTER_UP &&
+                 event.getActionMasked() != MotionEvent.ACTION_MOVE)) {
+            return null;
+        }
+
+        int action = event.getActionMasked();
+
+        if (action == MotionEvent.ACTION_POINTER_DOWN) {
+            action = MotionEvent.ACTION_DOWN;
+        } else if (action == MotionEvent.ACTION_POINTER_UP) {
+            action = MotionEvent.ACTION_UP;
+        }
+
+        // Map the information from the second pointer to the first.
+        return MotionEvent.obtain(mSecondPointerDownTime, event.getEventTime(), action,
+                event.getX(1), event.getY(1), event.getPressure(1), event.getSize(1),
+                event.getMetaState(), event.getXPrecision(), event.getYPrecision(),
+                event.getDeviceId(), event.getEdgeFlags());
+    }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 65a27c8..37a6c02 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4848,6 +4848,11 @@
     }
 
     @Override
+    public String getCaptivePortalServerUrl() {
+        return NetworkMonitor.getCaptivePortalServerUrl(mContext);
+    }
+
+    @Override
     public void startNattKeepalive(Network network, int intervalSeconds, Messenger messenger,
             IBinder binder, String srcAddr, int srcPort, String dstAddr) {
         enforceKeepalivePermission();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a4b13ed..093a33d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -35,6 +35,7 @@
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.os.Zygote;
+import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
@@ -255,7 +256,9 @@
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.content.pm.PackageManager.GET_PROVIDERS;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -1306,7 +1309,7 @@
     int mMemWatchDumpUid;
     String mTrackAllocationApp = null;
 
-    final long[] mTmpLong = new long[1];
+    final long[] mTmpLong = new long[2];
 
     static final class ProcessChangeItem {
         static final int CHANGE_ACTIVITIES = 1<<0;
@@ -1954,8 +1957,10 @@
                 break;
             }
             case SYSTEM_USER_UNLOCK_MSG: {
-                mSystemServiceManager.unlockUser(msg.arg1);
-                mRecentTasks.cleanupLocked(msg.arg1);
+                final int userId = msg.arg1;
+                mSystemServiceManager.unlockUser(userId);
+                mRecentTasks.cleanupLocked(userId);
+                installEncryptionUnawareProviders(userId);
                 break;
             }
             case SYSTEM_USER_CURRENT_MSG: {
@@ -2237,7 +2242,7 @@
                 }
 
                 int num = 0;
-                long[] tmp = new long[1];
+                long[] tmp = new long[2];
                 do {
                     ProcessRecord proc;
                     int procState;
@@ -2269,7 +2274,7 @@
                             if (pss != 0 && proc.thread != null && proc.setProcState == procState
                                     && proc.pid == pid && proc.lastPssTime == lastPssTime) {
                                 num++;
-                                recordPssSampleLocked(proc, procState, pss, tmp[0],
+                                recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1],
                                         SystemClock.uptimeMillis());
                             }
                         }
@@ -6586,8 +6591,10 @@
             Process.establishZygoteConnectionForAbi(abi);
             final String instructionSet = VMRuntime.getInstructionSet(abi);
             if (!completedIsas.contains(instructionSet)) {
-                if (mInstaller.markBootComplete(VMRuntime.getInstructionSet(abi)) != 0) {
-                    Slog.e(TAG, "Unable to mark boot complete for abi: " + abi);
+                try {
+                    mInstaller.markBootComplete(VMRuntime.getInstructionSet(abi));
+                } catch (InstallerException e) {
+                    Slog.e(TAG, "Unable to mark boot complete for abi: " + abi, e);
                 }
                 completedIsas.add(instructionSet);
             }
@@ -10824,6 +10831,49 @@
     }
 
     /**
+     * When a user is unlocked, we need to install encryption-unaware providers
+     * belonging to any running apps.
+     */
+    private void installEncryptionUnawareProviders(int userId) {
+        if (!StorageManager.isFileBasedEncryptionEnabled()) {
+            // TODO: eventually pivot this back to look at current user state,
+            // similar to the comment in UserManager.isUserUnlocked(), but for
+            // now, if we started apps when "unlocked" then unaware providers
+            // have already been spun up.
+            return;
+        }
+
+        synchronized (this) {
+            final int NP = mProcessNames.getMap().size();
+            for (int ip = 0; ip < NP; ip++) {
+                final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+                final int NA = apps.size();
+                for (int ia = 0; ia < NA; ia++) {
+                    final ProcessRecord app = apps.valueAt(ia);
+                    if (app.userId != userId || app.thread == null) continue;
+
+                    final int NG = app.pkgList.size();
+                    for (int ig = 0; ig < NG; ig++) {
+                        try {
+                            final String pkgName = app.pkgList.keyAt(ig);
+                            final PackageInfo pkgInfo = AppGlobals.getPackageManager()
+                                    .getPackageInfo(pkgName,
+                                            GET_PROVIDERS | MATCH_ENCRYPTION_UNAWARE, userId);
+                            if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) {
+                                for (ProviderInfo provInfo : pkgInfo.providers) {
+                                    Log.v(TAG, "Installing " + provInfo);
+                                    app.thread.scheduleInstallProvider(provInfo);
+                                }
+                            }
+                        } catch (RemoteException ignored) {
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * Allows apps to retrieve the MIME type of a URI.
      * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
      * users, then it does not need permission to access the ContentProvider.
@@ -12220,6 +12270,8 @@
                             sb.append(proc.processName);
                             sb.append(" in idle maint: pss=");
                             sb.append(proc.lastPss);
+                            sb.append(", swapPss=");
+                            sb.append(proc.lastSwapPss);
                             sb.append(", initialPss=");
                             sb.append(proc.initialIdlePss);
                             sb.append(", period=");
@@ -15127,6 +15179,7 @@
                 pw.print("state: cur="); pw.print(ProcessList.makeProcStateString(r.curProcState));
                 pw.print(" set="); pw.print(ProcessList.makeProcStateString(r.setProcState));
                 pw.print(" lastPss="); DebugUtils.printSizeValue(pw, r.lastPss*1024);
+                pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, r.lastSwapPss*1024);
                 pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, r.lastCachedPss*1024);
                 pw.println();
                 pw.print(prefix);
@@ -15280,32 +15333,35 @@
         final String label;
         final String shortLabel;
         final long pss;
+        final long swapPss;
         final int id;
         final boolean hasActivities;
         ArrayList<MemItem> subitems;
 
-        public MemItem(String _label, String _shortLabel, long _pss, int _id,
+        public MemItem(String _label, String _shortLabel, long _pss, long _swapPss, int _id,
                 boolean _hasActivities) {
             isProc = true;
             label = _label;
             shortLabel = _shortLabel;
             pss = _pss;
+            swapPss = _swapPss;
             id = _id;
             hasActivities = _hasActivities;
         }
 
-        public MemItem(String _label, String _shortLabel, long _pss, int _id) {
+        public MemItem(String _label, String _shortLabel, long _pss, long _swapPss, int _id) {
             isProc = false;
             label = _label;
             shortLabel = _shortLabel;
             pss = _pss;
+            swapPss = _swapPss;
             id = _id;
             hasActivities = false;
         }
     }
 
     static final void dumpMemItems(PrintWriter pw, String prefix, String tag,
-            ArrayList<MemItem> items, boolean sort, boolean isCompact) {
+            ArrayList<MemItem> items, boolean sort, boolean isCompact, boolean dumpSwapPss) {
         if (sort && !isCompact) {
             Collections.sort(items, new Comparator<MemItem>() {
                 @Override
@@ -15323,18 +15379,24 @@
         for (int i=0; i<items.size(); i++) {
             MemItem mi = items.get(i);
             if (!isCompact) {
-                pw.printf("%s%s: %s\n", prefix, stringifyKBSize(mi.pss), mi.label);
+                if (dumpSwapPss) {
+                    pw.printf("%s%s: %-60s (%s in swap)\n", prefix, stringifyKBSize(mi.pss),
+                            mi.label, stringifyKBSize(mi.swapPss));
+                } else {
+                    pw.printf("%s%s: %s\n", prefix, stringifyKBSize(mi.pss), mi.label);
+                }
             } else if (mi.isProc) {
                 pw.print("proc,"); pw.print(tag); pw.print(","); pw.print(mi.shortLabel);
-                pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss);
+                pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss); pw.print(",");
+                pw.print(dumpSwapPss ? mi.swapPss : "N/A");
                 pw.println(mi.hasActivities ? ",a" : ",e");
             } else {
                 pw.print(tag); pw.print(","); pw.print(mi.shortLabel); pw.print(",");
-                pw.println(mi.pss);
+                pw.println(mi.pss); pw.print(dumpSwapPss ? mi.swapPss : "N/A");
             }
             if (mi.subitems != null) {
-                dumpMemItems(pw, prefix + "           ", mi.shortLabel, mi.subitems,
-                        true, isCompact);
+                dumpMemItems(pw, prefix + "    ", mi.shortLabel, mi.subitems,
+                        true, isCompact, dumpSwapPss);
             }
         }
     }
@@ -15463,6 +15525,8 @@
         boolean isCompact = false;
         boolean localOnly = false;
         boolean packages = false;
+        boolean isCheckinRequest = false;
+        boolean dumpSwapPss = false;
 
         int opti = 0;
         while (opti < args.length) {
@@ -15475,6 +15539,7 @@
                 dumpDetails = true;
                 dumpFullDetails = true;
                 dumpDalvik = true;
+                dumpSwapPss = true;
             } else if ("-d".equals(opt)) {
                 dumpDalvik = true;
             } else if ("-c".equals(opt)) {
@@ -15482,22 +15547,29 @@
             } else if ("-s".equals(opt)) {
                 dumpDetails = true;
                 dumpSummaryOnly = true;
+            } else if ("-S".equals(opt)) {
+                dumpSwapPss = true;
             } else if ("--oom".equals(opt)) {
                 oomOnly = true;
             } else if ("--local".equals(opt)) {
                 localOnly = true;
             } else if ("--package".equals(opt)) {
                 packages = true;
+            } else if ("--checkin".equals(opt)) {
+                isCheckinRequest = true;
+
             } else if ("-h".equals(opt)) {
                 pw.println("meminfo dump options: [-a] [-d] [-c] [-s] [--oom] [process]");
                 pw.println("  -a: include all available information for each process.");
                 pw.println("  -d: include dalvik details.");
                 pw.println("  -c: dump in a compact machine-parseable representation.");
                 pw.println("  -s: dump only summary of application memory usage.");
+                pw.println("  -S: dump also SwapPss.");
                 pw.println("  --oom: only show processes organized by oom adj.");
                 pw.println("  --local: only collect details locally, don't call process.");
                 pw.println("  --package: interpret process arg as package, dumping all");
                 pw.println("             processes that have loaded that package.");
+                pw.println("  --checkin: dump data for a checkin");
                 pw.println("If [process] is specified it can be the name or ");
                 pw.println("pid of a specific process to dump.");
                 return;
@@ -15506,7 +15578,6 @@
             }
         }
 
-        final boolean isCheckinRequest = scanArgs(args, "--checkin");
         long uptime = SystemClock.uptimeMillis();
         long realtime = SystemClock.elapsedRealtime();
         final long[] tmpLong = new long[1];
@@ -15578,18 +15649,28 @@
         ArrayList<MemItem> procMems = new ArrayList<MemItem>();
         final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
         long nativePss = 0;
+        long nativeSwapPss = 0;
         long dalvikPss = 0;
+        long dalvikSwapPss = 0;
         long[] dalvikSubitemPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
                 EmptyArray.LONG;
+        long[] dalvikSubitemSwapPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
+                EmptyArray.LONG;
         long otherPss = 0;
+        long otherSwapPss = 0;
         long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
+        long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
 
         long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length];
+        long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length];
         ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
                 new ArrayList[DUMP_MEM_OOM_LABEL.length];
 
         long totalPss = 0;
+        long totalSwapPss = 0;
         long cachedPss = 0;
+        long cachedSwapPss = 0;
+        boolean hasSwapPss = false;
 
         Debug.MemoryInfo mi = null;
         for (int i = procs.size() - 1 ; i >= 0 ; i--) {
@@ -15613,6 +15694,7 @@
                 }
                 if (dumpDetails || (!brief && !oomOnly)) {
                     Debug.getMemoryInfo(pid, mi);
+                    hasSwapPss = mi.hasSwappedOutPss;
                 } else {
                     mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
                     mi.dalvikPrivateDirty = (int)tmpLong[0];
@@ -15640,6 +15722,7 @@
 
                 final long myTotalPss = mi.getTotalPss();
                 final long myTotalUss = mi.getTotalUss();
+                final long myTotalSwapPss = mi.getTotalSwappedOutPss();
 
                 synchronized (this) {
                     if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
@@ -15650,32 +15733,43 @@
 
                 if (!isCheckinRequest && mi != null) {
                     totalPss += myTotalPss;
+                    totalSwapPss += myTotalSwapPss;
                     MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
-                            (hasActivities ? " / activities)" : ")"),
-                            r.processName, myTotalPss, pid, hasActivities);
+                            (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
+                            myTotalSwapPss, pid, hasActivities);
                     procMems.add(pssItem);
                     procMemsMap.put(pid, pssItem);
 
                     nativePss += mi.nativePss;
+                    nativeSwapPss += mi.nativeSwappedOutPss;
                     dalvikPss += mi.dalvikPss;
+                    dalvikSwapPss += mi.dalvikSwappedOutPss;
                     for (int j=0; j<dalvikSubitemPss.length; j++) {
                         dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                        dalvikSubitemSwapPss[j] +=
+                                mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
                     }
                     otherPss += mi.otherPss;
+                    otherSwapPss += mi.otherSwappedOutPss;
                     for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                         long mem = mi.getOtherPss(j);
                         miscPss[j] += mem;
                         otherPss -= mem;
+                        mem = mi.getOtherSwappedOutPss(j);
+                        miscSwapPss[j] += mem;
+                        otherSwapPss -= mem;
                     }
 
                     if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
                         cachedPss += myTotalPss;
+                        cachedSwapPss += myTotalSwapPss;
                     }
 
                     for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) {
                         if (oomAdj <= DUMP_MEM_OOM_ADJ[oomIndex]
                                 || oomIndex == (oomPss.length-1)) {
                             oomPss[oomIndex] += myTotalPss;
+                            oomSwapPss[oomIndex] += myTotalSwapPss;
                             if (oomProcs[oomIndex] == null) {
                                 oomProcs[oomIndex] = new ArrayList<MemItem>();
                             }
@@ -15710,26 +15804,35 @@
                         }
 
                         final long myTotalPss = mi.getTotalPss();
+                        final long myTotalSwapPss = mi.getTotalSwappedOutPss();
                         totalPss += myTotalPss;
                         nativeProcTotalPss += myTotalPss;
 
                         MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
-                                st.name, myTotalPss, st.pid, false);
+                                st.name, myTotalPss, mi.getSummaryTotalSwapPss(), st.pid, false);
                         procMems.add(pssItem);
 
                         nativePss += mi.nativePss;
+                        nativeSwapPss += mi.nativeSwappedOutPss;
                         dalvikPss += mi.dalvikPss;
+                        dalvikSwapPss += mi.dalvikSwappedOutPss;
                         for (int j=0; j<dalvikSubitemPss.length; j++) {
-                            dalvikSubitemPss[j] += mi.getOtherPss(
-                                    Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                            dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                            dalvikSubitemSwapPss[j] +=
+                                    mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
                         }
                         otherPss += mi.otherPss;
+                        otherSwapPss += mi.otherSwappedOutPss;
                         for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                             long mem = mi.getOtherPss(j);
                             miscPss[j] += mem;
                             otherPss -= mem;
+                            mem = mi.getOtherSwappedOutPss(j);
+                            miscSwapPss[j] += mem;
+                            otherSwapPss -= mem;
                         }
                         oomPss[0] += myTotalPss;
+                        oomSwapPss[0] += myTotalSwapPss;
                         if (oomProcs[0] == null) {
                             oomProcs[0] = new ArrayList<MemItem>();
                         }
@@ -15740,21 +15843,23 @@
 
             ArrayList<MemItem> catMems = new ArrayList<MemItem>();
 
-            catMems.add(new MemItem("Native", "Native", nativePss, -1));
-            final MemItem dalvikItem = new MemItem("Dalvik", "Dalvik", dalvikPss, -2);
+            catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, -1));
+            final MemItem dalvikItem =
+                    new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, -2);
             if (dalvikSubitemPss.length > 0) {
                 dalvikItem.subitems = new ArrayList<MemItem>();
                 for (int j=0; j<dalvikSubitemPss.length; j++) {
                     final String name = Debug.MemoryInfo.getOtherLabel(
                             Debug.MemoryInfo.NUM_OTHER_STATS + j);
-                    dalvikItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], j));
+                    dalvikItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j],
+                                    dalvikSubitemSwapPss[j], j));
                 }
             }
             catMems.add(dalvikItem);
-            catMems.add(new MemItem("Unknown", "Unknown", otherPss, -3));
+            catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, -3));
             for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                 String label = Debug.MemoryInfo.getOtherLabel(j);
-                catMems.add(new MemItem(label, label, miscPss[j], j));
+                catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], j));
             }
 
             ArrayList<MemItem> oomMems = new ArrayList<MemItem>();
@@ -15762,30 +15867,31 @@
                 if (oomPss[j] != 0) {
                     String label = isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j]
                             : DUMP_MEM_OOM_LABEL[j];
-                    MemItem item = new MemItem(label, label, oomPss[j],
+                    MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j],
                             DUMP_MEM_OOM_ADJ[j]);
                     item.subitems = oomProcs[j];
                     oomMems.add(item);
                 }
             }
 
+            dumpSwapPss = dumpSwapPss && hasSwapPss && totalSwapPss != 0;
             if (!brief && !oomOnly && !isCompact) {
                 pw.println();
                 pw.println("Total PSS by process:");
-                dumpMemItems(pw, "  ", "proc", procMems, true, isCompact);
+                dumpMemItems(pw, "  ", "proc", procMems, true, isCompact, dumpSwapPss);
                 pw.println();
             }
             if (!isCompact) {
                 pw.println("Total PSS by OOM adjustment:");
             }
-            dumpMemItems(pw, "  ", "oom", oomMems, false, isCompact);
+            dumpMemItems(pw, "  ", "oom", oomMems, false, isCompact, dumpSwapPss);
             if (!brief && !oomOnly) {
                 PrintWriter out = categoryPw != null ? categoryPw : pw;
                 if (!isCompact) {
                     out.println();
                     out.println("Total PSS by category:");
                 }
-                dumpMemItems(out, "  ", "cat", catMems, true, isCompact);
+                dumpMemItems(out, "  ", "cat", catMems, true, isCompact, dumpSwapPss);
             }
             if (!isCompact) {
                 pw.println();
@@ -18291,9 +18397,12 @@
      * dialog / global actions also might want different behaviors.
      */
     private static final boolean shouldShowDialogs(Configuration config) {
-        return !(config.keyboard == Configuration.KEYBOARD_NOKEYS
-                && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
-                && config.navigation == Configuration.NAVIGATION_NONAV);
+        final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
+                                   && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
+                                   && config.navigation == Configuration.NAVIGATION_NONAV);
+        final boolean uiIsNotCarType = !((config.uiMode & Configuration.UI_MODE_TYPE_MASK)
+                                    == Configuration.UI_MODE_TYPE_CAR);
+        return inputMethodExists && uiIsNotCarType;
     }
 
     @Override
@@ -19105,8 +19214,10 @@
     /**
      * Record new PSS sample for a process.
      */
-    void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long now) {
-        EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024);
+    void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long swapPss,
+            long now) {
+        EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
+                swapPss * 1024);
         proc.lastPssTime = now;
         proc.baseProcessTracker.addPss(pss, uss, true, proc.pkgList);
         if (DEBUG_PSS) Slog.d(TAG_PSS,
@@ -19116,8 +19227,10 @@
             proc.initialIdlePss = pss;
         }
         proc.lastPss = pss;
+        proc.lastSwapPss = swapPss;
         if (procState >= ActivityManager.PROCESS_STATE_HOME) {
             proc.lastCachedPss = pss;
+            proc.lastCachedSwapPss = swapPss;
         }
 
         final SparseArray<Pair<Long, String>> watchUids
@@ -19553,7 +19666,7 @@
                 // states, which well tend to give noisy data.
                 long start = SystemClock.uptimeMillis();
                 long pss = Debug.getPss(app.pid, mTmpLong, null);
-                recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], now);
+                recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], mTmpLong[1], now);
                 mPendingPssProcesses.remove(app);
                 Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState
                         + " to " + app.curProcState + ": "
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 0397553..f2e8d09 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -100,6 +100,6 @@
 30045 am_pre_boot (User|1|5),(Package|3)
 
 # Report collection of global memory state
-30046 am_meminfo (CachedKb|2|2),(FreeKb|2|2),(ZramKb|2|2),(KernelKb|2|2),(NativeKb|2|2)
+30046 am_meminfo (Cached|2|2),(Free|2|2),(Zram|2|2),(Kernel|2|2),(Native|2|2)
 # Report collection of memory used by a process
-30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(PssKb|2|2),(UssKb|2|2)
+30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(Pss|2|2),(Uss|2|2),(SwapPss|2|2)
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 4bfe300..b4aa4cf 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -82,7 +82,9 @@
     long lastStateTime;         // Last time setProcState changed
     long initialIdlePss;        // Initial memory pss of process for idle maintenance.
     long lastPss;               // Last computed memory pss.
+    long lastSwapPss;           // Last computed SwapPss.
     long lastCachedPss;         // Last computed pss when in cached state.
+    long lastCachedSwapPss;     // Last computed SwapPss when in cached state.
     int maxAdj;                 // Maximum OOM adjustment for this process
     int curRawAdj;              // Current OOM unlimited adjustment for this process
     int setRawAdj;              // Last set OOM unlimited adjustment for this process
@@ -257,7 +259,9 @@
         pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
                 pw.print(" lruSeq="); pw.print(lruSeq);
                 pw.print(" lastPss="); DebugUtils.printSizeValue(pw, lastPss*1024);
+                pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, lastSwapPss*1024);
                 pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, lastCachedPss*1024);
+                pw.print(" lastCachedSwapPss="); DebugUtils.printSizeValue(pw, lastCachedSwapPss*1024);
                 pw.println();
         pw.print(prefix); pw.print("cached="); pw.print(cached);
                 pw.print(" empty="); pw.println(empty);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b8cbecb..9331dd8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -58,10 +58,12 @@
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
 import android.media.AudioPort;
+import android.media.AudioRecordConfiguration;
 import android.media.AudioRoutesInfo;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioRoutesObserver;
 import android.media.IAudioService;
+import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
 import android.media.IVolumeController;
 import android.media.MediaPlayer;
@@ -706,6 +708,8 @@
         LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
 
         mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
+
+        mRecordMonitor.initMonitor();
     }
 
     public void systemReady() {
@@ -6165,7 +6169,7 @@
     }
 
     //======================
-    // Audio policy callback from AudioSystem
+    // Audio policy callbacks from AudioSystem for dynamic policies
     //======================
     private final AudioSystem.DynamicPolicyCallback mDynPolicyCallback =
             new AudioSystem.DynamicPolicyCallback() {
@@ -6194,7 +6198,23 @@
                 }
             }
         }
+    }
 
+    //======================
+    // Audio policy callbacks from AudioSystem for recording configuration updates
+    //======================
+    private final RecordingActivityMonitor mRecordMonitor = new RecordingActivityMonitor();
+
+    public void registerRecordingCallback(IRecordingConfigDispatcher rcdb) {
+        mRecordMonitor.registerRecordingCallback(rcdb);
+    }
+
+    public void unregisterRecordingCallback(IRecordingConfigDispatcher rcdb) {
+        mRecordMonitor.unregisterRecordingCallback(rcdb);
+    }
+
+    public AudioRecordConfiguration[] getActiveRecordConfigurations() {
+        return mRecordMonitor.getActiveRecordConfigurations();
     }
 
     //======================
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
new file mode 100644
index 0000000..5806f3f
--- /dev/null
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.audio;
+
+import android.media.AudioManager;
+import android.media.AudioRecordConfiguration;
+import android.media.AudioSystem;
+import android.media.IRecordingConfigDispatcher;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Class to receive and dispatch updates from AudioSystem about recording configurations.
+ */
+public final class RecordingActivityMonitor implements AudioSystem.AudioRecordingCallback {
+
+    public final static String TAG = "AudioService.RecordingActivityMonitor";
+
+    private ArrayList<RecMonitorClient> mClients = new ArrayList<RecMonitorClient>();
+
+    private HashMap<Integer, AudioRecordConfiguration> mRecordConfigs =
+            new HashMap<Integer, AudioRecordConfiguration>();
+
+    RecordingActivityMonitor() {
+        RecMonitorClient.sMonitor = this;
+    }
+
+    /**
+     * Implementation of android.media.AudioSystem.AudioRecordingCallback
+     */
+    public void onRecordingConfigurationChanged(int event, int session, int source) {
+        if (updateSnapshot(event, session, source)) {
+            final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
+            synchronized(mClients) {
+                while (clientIterator.hasNext()) {
+                    try {
+                        clientIterator.next().mDispatcherCb.dispatchRecordingConfigChange();
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Could not call dispatchRecordingConfigChange() on client", e);
+                    }
+                }
+            }
+        }
+    }
+
+    void initMonitor() {
+        AudioSystem.setRecordingCallback(this);
+    }
+
+    void registerRecordingCallback(IRecordingConfigDispatcher rcdb) {
+        if (rcdb == null) {
+            return;
+        }
+        synchronized(mClients) {
+            final RecMonitorClient rmc = new RecMonitorClient(rcdb);
+            if (rmc.init()) {
+                mClients.add(rmc);
+            }
+        }
+    }
+
+    void unregisterRecordingCallback(IRecordingConfigDispatcher rcdb) {
+        if (rcdb == null) {
+            return;
+        }
+        synchronized(mClients) {
+            final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
+            while (clientIterator.hasNext()) {
+                RecMonitorClient rmc = clientIterator.next();
+                if (rcdb.equals(rmc.mDispatcherCb)) {
+                    rmc.release();
+                    clientIterator.remove();
+                    break;
+                }
+            }
+        }
+    }
+
+    AudioRecordConfiguration[] getActiveRecordConfigurations() {
+        synchronized(mRecordConfigs) {
+            return mRecordConfigs.values().toArray(new AudioRecordConfiguration[0]);
+        }
+    }
+
+    /**
+     * Update the internal "view" of the active recording sessions
+     * @param event
+     * @param session
+     * @param source
+     * @return true if the list of active recording sessions has been modified, false otherwise.
+     */
+    private boolean updateSnapshot(int event, int session, int source) {
+        synchronized(mRecordConfigs) {
+            switch (event) {
+            case AudioManager.RECORD_CONFIG_EVENT_STOP:
+                // return failure if an unknown recording session stopped
+                return (mRecordConfigs.remove(new Integer(session)) != null);
+            case AudioManager.RECORD_CONFIG_EVENT_START:
+                if (mRecordConfigs.containsKey(new Integer(session))) {
+                    // start of session that's already tracked, not worth an update
+                    // TO DO in the future when tracking record format: there might be a record
+                    //       format change during a recording that requires reporting
+                    return false;
+                } else {
+                    mRecordConfigs.put(new Integer(session),
+                            new AudioRecordConfiguration(session, source));
+                    return true;
+                }
+            default:
+                Log.e(TAG, String.format("Unknown event %d for session %d, source %d",
+                        event, session, source));
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Inner class to track clients that want to be notified of recording updates
+     */
+    private final static class RecMonitorClient implements IBinder.DeathRecipient {
+
+        // can afford to be static because only one RecordingActivityMonitor ever instantiated
+        static RecordingActivityMonitor sMonitor;
+
+        final IRecordingConfigDispatcher mDispatcherCb;
+
+        RecMonitorClient(IRecordingConfigDispatcher rcdb) {
+            mDispatcherCb = rcdb;
+        }
+
+        public void binderDied() {
+            Log.w(TAG, "client died");
+            sMonitor.unregisterRecordingCallback(mDispatcherCb);
+        }
+
+        boolean init() {
+            try {
+                mDispatcherCb.asBinder().linkToDeath(this, 0);
+                return true;
+            } catch (RemoteException e) {
+                Log.w(TAG, "Could not link to client death", e);
+                return false;
+            }
+        }
+
+        void release() {
+            mDispatcherCb.asBinder().unlinkToDeath(this, 0);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 3a10dbe..4504bdb 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -223,7 +223,6 @@
     private final AlarmManager mAlarmManager;
     private final NetworkRequest mDefaultRequest;
 
-    private String mServer;
     private boolean mIsCaptivePortalCheckEnabled = false;
 
     // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app.
@@ -265,10 +264,6 @@
         addState(mLingeringState, mDefaultState);
         setInitialState(mDefaultState);
 
-        mServer = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.CAPTIVE_PORTAL_SERVER);
-        if (mServer == null) mServer = DEFAULT_SERVER;
-
         mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
 
         mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
@@ -622,6 +617,13 @@
         }
     }
 
+    public static String getCaptivePortalServerUrl(Context context) {
+        String server = Settings.Global.getString(context.getContentResolver(),
+                Settings.Global.CAPTIVE_PORTAL_SERVER);
+        if (server == null) server = DEFAULT_SERVER;
+        return "http://" + server + "/generate_204";
+    }
+
     /**
      * Do a URL fetch on a known server to see if we get the data we expect.
      * Returns HTTP response code.
@@ -633,9 +635,9 @@
         HttpURLConnection urlConnection = null;
         int httpResponseCode = 599;
         try {
-            URL url = new URL("http", mServer, "/generate_204");
+            URL url = new URL(getCaptivePortalServerUrl(mContext));
             // On networks with a PAC instead of fetching a URL that should result in a 204
-            // reponse, we instead simply fetch the PAC script.  This is done for a few reasons:
+            // response, we instead simply fetch the PAC script.  This is done for a few reasons:
             // 1. At present our PAC code does not yet handle multiple PACs on multiple networks
             //    until something like https://android-review.googlesource.com/#/c/115180/ lands.
             //    Network.openConnection() will ignore network-specific PACs and instead fetch
@@ -644,7 +646,8 @@
             // 2. To proxy the generate_204 fetch through a PAC would require a number of things
             //    happen before the fetch can commence, namely:
             //        a) the PAC script be fetched
-            //        b) a PAC script resolver service be fired up and resolve mServer
+            //        b) a PAC script resolver service be fired up and resolve the captive portal
+            //           server.
             //    Network validation could be delayed until these prerequisities are satisifed or
             //    could simply be left to race them.  Neither is an optimal solution.
             // 3. PAC scripts are sometimes used to block or restrict Internet access and may in
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 99a051a..190eca6 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -20,14 +20,14 @@
 import android.content.Context;
 import android.content.pm.PackageStats;
 import android.os.Build;
-import android.text.TextUtils;
 import android.util.Slog;
 
-import dalvik.system.VMRuntime;
-
 import com.android.internal.os.InstallerConnection;
+import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.server.SystemService;
 
+import dalvik.system.VMRuntime;
+
 public final class Installer extends SystemService {
     private static final String TAG = "Installer";
 
@@ -46,6 +46,11 @@
     /** Run the application with the JIT compiler */
     public static final int DEXOPT_USEJIT       = 1 << 5;
 
+    public static final int FLAG_DE_STORAGE = 1 << 0;
+    public static final int FLAG_CE_STORAGE = 1 << 1;
+    public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 2;
+    public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 3;
+
     private final InstallerConnection mInstaller;
 
     public Installer(Context context) {
@@ -67,423 +72,137 @@
         mInstaller.waitForConnection();
     }
 
-    private static String escapeNull(String arg) {
-        if (TextUtils.isEmpty(arg)) {
-            return "!";
-        } else {
-            if (arg.indexOf('\0') != -1 || arg.indexOf(' ') != -1) {
-                throw new IllegalArgumentException(arg);
-            }
-            return arg;
-        }
+    public void createAppData(String uuid, String pkgname, int userid, int flags, int appid,
+            String seinfo) throws InstallerException {
+        mInstaller.execute("create_app_data", uuid, pkgname, userid, flags, appid, seinfo);
     }
 
-    @Deprecated
-    public int install(String name, int uid, int gid, String seinfo) {
-        return install(null, name, uid, gid, seinfo);
+    public void restoreconAppData(String uuid, String pkgname, int userid, int flags, int appid,
+            String seinfo) throws InstallerException {
+        mInstaller.execute("restorecon_app_data", uuid, pkgname, userid, flags, appid,
+                seinfo);
     }
 
-    public int install(String uuid, String name, int uid, int gid, String seinfo) {
-        StringBuilder builder = new StringBuilder("install");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(uid);
-        builder.append(' ');
-        builder.append(gid);
-        builder.append(' ');
-        builder.append(seinfo != null ? seinfo : "!");
-        return mInstaller.execute(builder.toString());
+    public void clearAppData(String uuid, String pkgname, int userid, int flags)
+            throws InstallerException {
+        mInstaller.execute("clear_app_data", uuid, pkgname, userid, flags);
     }
 
-    public int dexopt(String apkPath, int uid, String instructionSet,
-            int dexoptNeeded, int dexFlags) {
-        if (!isValidInstructionSet(instructionSet)) {
-            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-            return -1;
-        }
-
-        return mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags);
+    public void destroyAppData(String uuid, String pkgname, int userid, int flags)
+            throws InstallerException {
+        mInstaller.execute("destroy_app_data", uuid, pkgname, userid, flags);
     }
 
-    public int dexopt(String apkPath, int uid, String pkgName, String instructionSet,
-            int dexoptNeeded, @Nullable String outputPath, int dexFlags) {
-        if (!isValidInstructionSet(instructionSet)) {
-            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-            return -1;
-        }
-        return mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
-                outputPath, dexFlags);
+    public void moveCompleteApp(String from_uuid, String to_uuid, String package_name,
+            String data_app_name, int appid, String seinfo) throws InstallerException {
+        mInstaller.execute("move_complete_app", from_uuid, to_uuid, package_name,
+                data_app_name, appid, seinfo);
     }
 
-    public int idmap(String targetApkPath, String overlayApkPath, int uid) {
-        StringBuilder builder = new StringBuilder("idmap");
-        builder.append(' ');
-        builder.append(targetApkPath);
-        builder.append(' ');
-        builder.append(overlayApkPath);
-        builder.append(' ');
-        builder.append(uid);
-        return mInstaller.execute(builder.toString());
-    }
-
-    public int movedex(String srcPath, String dstPath, String instructionSet) {
-        if (!isValidInstructionSet(instructionSet)) {
-            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-            return -1;
-        }
-
-        StringBuilder builder = new StringBuilder("movedex");
-        builder.append(' ');
-        builder.append(srcPath);
-        builder.append(' ');
-        builder.append(dstPath);
-        builder.append(' ');
-        builder.append(instructionSet);
-        return mInstaller.execute(builder.toString());
-    }
-
-    public int rmdex(String codePath, String instructionSet) {
-        if (!isValidInstructionSet(instructionSet)) {
-            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-            return -1;
-        }
-
-        StringBuilder builder = new StringBuilder("rmdex");
-        builder.append(' ');
-        builder.append(codePath);
-        builder.append(' ');
-        builder.append(instructionSet);
-        return mInstaller.execute(builder.toString());
-    }
-
-    /**
-     * Removes packageDir or its subdirectory
-     */
-    public int rmPackageDir(String packageDir) {
-        StringBuilder builder = new StringBuilder("rmpackagedir");
-        builder.append(' ');
-        builder.append(packageDir);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int remove(String name, int userId) {
-        return remove(null, name, userId);
-    }
-
-    public int remove(String uuid, String name, int userId) {
-        StringBuilder builder = new StringBuilder("remove");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(userId);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int fixUid(String name, int uid, int gid) {
-        return fixUid(null, name, uid, gid);
-    }
-
-    public int fixUid(String uuid, String name, int uid, int gid) {
-        StringBuilder builder = new StringBuilder("fixuid");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(uid);
-        builder.append(' ');
-        builder.append(gid);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int deleteCacheFiles(String name, int userId) {
-        return deleteCacheFiles(null, name, userId);
-    }
-
-    public int deleteCacheFiles(String uuid, String name, int userId) {
-        StringBuilder builder = new StringBuilder("rmcache");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(userId);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int deleteCodeCacheFiles(String name, int userId) {
-        return deleteCodeCacheFiles(null, name, userId);
-    }
-
-    public int deleteCodeCacheFiles(String uuid, String name, int userId) {
-        StringBuilder builder = new StringBuilder("rmcodecache");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(userId);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int createUserData(String name, int uid, int userId, String seinfo) {
-        return createUserData(null, name, uid, userId, seinfo);
-    }
-
-    public int createUserData(String uuid, String name, int uid, int userId, String seinfo) {
-        StringBuilder builder = new StringBuilder("mkuserdata");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(uid);
-        builder.append(' ');
-        builder.append(userId);
-        builder.append(' ');
-        builder.append(seinfo != null ? seinfo : "!");
-        return mInstaller.execute(builder.toString());
-    }
-
-    public int createUserConfig(int userId) {
-        StringBuilder builder = new StringBuilder("mkuserconfig");
-        builder.append(' ');
-        builder.append(userId);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int removeUserDataDirs(int userId) {
-        return removeUserDataDirs(null, userId);
-    }
-
-    public int removeUserDataDirs(String uuid, int userId) {
-        StringBuilder builder = new StringBuilder("rmuser");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(userId);
-        return mInstaller.execute(builder.toString());
-    }
-
-    public int copyCompleteApp(String fromUuid, String toUuid, String packageName,
-            String dataAppName, int appId, String seinfo) {
-        StringBuilder builder = new StringBuilder("cpcompleteapp");
-        builder.append(' ');
-        builder.append(escapeNull(fromUuid));
-        builder.append(' ');
-        builder.append(escapeNull(toUuid));
-        builder.append(' ');
-        builder.append(packageName);
-        builder.append(' ');
-        builder.append(dataAppName);
-        builder.append(' ');
-        builder.append(appId);
-        builder.append(' ');
-        builder.append(seinfo);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int clearUserData(String name, int userId) {
-        return clearUserData(null, name, userId);
-    }
-
-    public int clearUserData(String uuid, String name, int userId) {
-        StringBuilder builder = new StringBuilder("rmuserdata");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(userId);
-        return mInstaller.execute(builder.toString());
-    }
-
-    public int markBootComplete(String instructionSet) {
-        if (!isValidInstructionSet(instructionSet)) {
-            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-            return -1;
-        }
-
-        StringBuilder builder = new StringBuilder("markbootcomplete");
-        builder.append(' ');
-        builder.append(instructionSet);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int freeCache(long freeStorageSize) {
-        return freeCache(null, freeStorageSize);
-    }
-
-    public int freeCache(String uuid, long freeStorageSize) {
-        StringBuilder builder = new StringBuilder("freecache");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(String.valueOf(freeStorageSize));
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
-            String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) {
-        return getSizeInfo(null, pkgName, persona, apkPath, libDirPath, fwdLockApkPath, asecPath,
-                instructionSets, pStats);
-    }
-
-    public int getSizeInfo(String uuid, String pkgName, int persona, String apkPath,
+    public void getAppSize(String uuid, String pkgname, int userid, int flags, String apkPath,
             String libDirPath, String fwdLockApkPath, String asecPath, String[] instructionSets,
-            PackageStats pStats) {
+            PackageStats pStats) throws InstallerException {
         for (String instructionSet : instructionSets) {
-            if (!isValidInstructionSet(instructionSet)) {
-                Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-                return -1;
-            }
+            assertValidInstructionSet(instructionSet);
         }
 
-        StringBuilder builder = new StringBuilder("getsize");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(pkgName);
-        builder.append(' ');
-        builder.append(persona);
-        builder.append(' ');
-        builder.append(apkPath);
-        builder.append(' ');
         // TODO: Extend getSizeInfo to look at the full subdirectory tree,
         // not just the first level.
-        builder.append(libDirPath != null ? libDirPath : "!");
-        builder.append(' ');
-        builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
-        builder.append(' ');
-        builder.append(asecPath != null ? asecPath : "!");
-        builder.append(' ');
         // TODO: Extend getSizeInfo to look at *all* instrution sets, not
         // just the primary.
-        builder.append(instructionSets[0]);
-
-        String s = mInstaller.transact(builder.toString());
-        String res[] = s.split(" ");
+        final String rawRes = mInstaller.executeForResult("get_app_size", uuid, pkgname, userid,
+                flags, apkPath, libDirPath, fwdLockApkPath, asecPath, instructionSets[0]);
+        final String res[] = rawRes.split(" ");
 
         if ((res == null) || (res.length != 5)) {
-            return -1;
+            throw new InstallerException("Invalid size result: " + rawRes);
         }
         try {
             pStats.codeSize = Long.parseLong(res[1]);
             pStats.dataSize = Long.parseLong(res[2]);
             pStats.cacheSize = Long.parseLong(res[3]);
             pStats.externalCodeSize = Long.parseLong(res[4]);
-            return Integer.parseInt(res[0]);
         } catch (NumberFormatException e) {
-            return -1;
+            throw new InstallerException("Invalid size result: " + rawRes);
         }
     }
 
-    public int moveFiles() {
-        return mInstaller.execute("movefiles");
+    public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
+            int dexFlags) throws InstallerException {
+        assertValidInstructionSet(instructionSet);
+        mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags);
     }
 
-    @Deprecated
-    public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath32, int userId) {
-        return linkNativeLibraryDirectory(null, dataPath, nativeLibPath32, userId);
+    public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+            int dexoptNeeded, @Nullable String outputPath, int dexFlags)
+                    throws InstallerException {
+        assertValidInstructionSet(instructionSet);
+        mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
+                outputPath, dexFlags);
+    }
+
+    public void idmap(String targetApkPath, String overlayApkPath, int uid)
+            throws InstallerException {
+        mInstaller.execute("idmap", targetApkPath, overlayApkPath, uid);
+    }
+
+    public void rmdex(String codePath, String instructionSet) throws InstallerException {
+        assertValidInstructionSet(instructionSet);
+        mInstaller.execute("rmdex", codePath, instructionSet);
+    }
+
+    public void rmPackageDir(String packageDir) throws InstallerException {
+        mInstaller.execute("rmpackagedir", packageDir);
+    }
+
+    public void createUserConfig(int userid) throws InstallerException {
+        mInstaller.execute("mkuserconfig", userid);
+    }
+
+    public void removeUserDataDirs(String uuid, int userid) throws InstallerException {
+        mInstaller.execute("rmuser", uuid, userid);
+    }
+
+    public void markBootComplete(String instructionSet) throws InstallerException {
+        assertValidInstructionSet(instructionSet);
+        mInstaller.execute("markbootcomplete", instructionSet);
+    }
+
+    public void freeCache(String uuid, long freeStorageSize) throws InstallerException {
+        mInstaller.execute("freecache", uuid, freeStorageSize);
+    }
+
+    public void moveFiles() throws InstallerException {
+        mInstaller.execute("movefiles");
     }
 
     /**
-     * Links the 32 bit native library directory in an application's data directory to the
-     * real location for backward compatibility. Note that no such symlink is created for
-     * 64 bit shared libraries.
-     *
-     * @return -1 on error
+     * Links the 32 bit native library directory in an application's data
+     * directory to the real location for backward compatibility. Note that no
+     * such symlink is created for 64 bit shared libraries.
      */
-    public int linkNativeLibraryDirectory(String uuid, String dataPath, String nativeLibPath32,
-            int userId) {
-        if (dataPath == null) {
-            Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null");
-            return -1;
-        } else if (nativeLibPath32 == null) {
-            Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null");
-            return -1;
-        }
-
-        StringBuilder builder = new StringBuilder("linklib");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(dataPath);
-        builder.append(' ');
-        builder.append(nativeLibPath32);
-        builder.append(' ');
-        builder.append(userId);
-
-        return mInstaller.execute(builder.toString());
+    public void linkNativeLibraryDirectory(String uuid, String dataPath, String nativeLibPath32,
+            int userId) throws InstallerException {
+        mInstaller.execute("linklib", uuid, dataPath, nativeLibPath32, userId);
     }
 
-    @Deprecated
-    public boolean restoreconData(String pkgName, String seinfo, int uid) {
-        return restoreconData(null, pkgName, seinfo, uid);
+    public void createOatDir(String oatDir, String dexInstructionSet)
+            throws InstallerException {
+        mInstaller.execute("createoatdir", oatDir, dexInstructionSet);
     }
 
-    public boolean restoreconData(String uuid, String pkgName, String seinfo, int uid) {
-        StringBuilder builder = new StringBuilder("restorecondata");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(pkgName);
-        builder.append(' ');
-        builder.append(seinfo != null ? seinfo : "!");
-        builder.append(' ');
-        builder.append(uid);
-        return (mInstaller.execute(builder.toString()) == 0);
+    public void linkFile(String relativePath, String fromBase, String toBase)
+            throws InstallerException {
+        mInstaller.execute("linkfile", relativePath, fromBase, toBase);
     }
 
-    public int createOatDir(String oatDir, String dexInstructionSet) {
-        StringBuilder builder = new StringBuilder("createoatdir");
-        builder.append(' ');
-        builder.append(oatDir);
-        builder.append(' ');
-        builder.append(dexInstructionSet);
-        return mInstaller.execute(builder.toString());
-    }
-
-
-    public int linkFile(String relativePath, String fromBase, String toBase) {
-        StringBuilder builder = new StringBuilder("linkfile");
-        builder.append(' ');
-        builder.append(relativePath);
-        builder.append(' ');
-        builder.append(fromBase);
-        builder.append(' ');
-        builder.append(toBase);
-        return mInstaller.execute(builder.toString());
-    }
-
-    /**
-     * Returns true iff. {@code instructionSet} is a valid instruction set.
-     */
-    private static boolean isValidInstructionSet(String instructionSet) {
-        if (instructionSet == null) {
-            return false;
-        }
-
+    private static void assertValidInstructionSet(String instructionSet)
+            throws InstallerException {
         for (String abi : Build.SUPPORTED_ABIS) {
-            if (instructionSet.equals(VMRuntime.getInstructionSet(abi))) {
-                return true;
+            if (VMRuntime.getInstructionSet(abi).equals(instructionSet)) {
+                return;
             }
         }
-
-        return false;
+        throw new InstallerException("Invalid instruction set: " + instructionSet);
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index d29a623..b45a922 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -27,6 +27,8 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.os.InstallerConnection.InstallerException;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -166,12 +168,13 @@
                             | (vmSafeMode ? DEXOPT_SAFEMODE : 0)
                             | (debuggable ? DEXOPT_DEBUGGABLE : 0)
                             | DEXOPT_BOOTCOMPLETE;
-                    final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
-                            pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, dexFlags);
-
-                    // Dex2oat might fail due to compiler / verifier errors.
-                    if (ret == 0) {
+                    try {
+                        mPackageManagerService.mInstaller.dexopt(path, sharedGid,
+                                pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir,
+                                dexFlags);
                         performedDexOpt = true;
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Failed to dexopt", e);
                     }
                 }
             }
@@ -210,8 +213,13 @@
         File codePath = new File(pkg.codePath);
         if (codePath.isDirectory()) {
             File oatDir = getOatDir(codePath);
-            mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),
-                    dexInstructionSet);
+            try {
+                mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),
+                        dexInstructionSet);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to create oat dir", e);
+                return null;
+            }
             return oatDir.getAbsolutePath();
         }
         return null;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 55b8bf2..23a58d0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -258,11 +258,7 @@
         for (File stage : unclaimedStages) {
             Slog.w(TAG, "Deleting orphan stage " + stage);
             synchronized (mPm.mInstallLock) {
-                if (stage.isDirectory()) {
-                    mPm.mInstaller.rmPackageDir(stage.getAbsolutePath());
-                } else {
-                    stage.delete();
-                }
+                mPm.removeCodePathLI(stage);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 1655cb6..b84ffa3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -69,6 +69,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
+import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
@@ -832,9 +833,14 @@
         throw new IOException("File: " + pathStr + " outside base: " + baseStr);
     }
 
-    private void createOatDirs(List<String> instructionSets, File fromDir) {
+    private void createOatDirs(List<String> instructionSets, File fromDir)
+            throws PackageManagerException {
         for (String instructionSet : instructionSets) {
-            mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
+            try {
+                mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
+            } catch (InstallerException e) {
+                throw PackageManagerException.from(e);
+            }
         }
     }
 
@@ -842,13 +848,12 @@
             throws IOException {
         for (File fromFile : fromFiles) {
             final String relativePath = getRelativePath(fromFile, fromDir);
-            final int ret = mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
-                    toDir.getAbsolutePath());
-
-            if (ret < 0) {
-                // installd will log failure details.
+            try {
+                mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
+                        toDir.getAbsolutePath());
+            } catch (InstallerException e) {
                 throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
-                        + fromDir + ", " + toDir + ")");
+                        + fromDir + ", " + toDir + ")", e);
             }
         }
 
@@ -1041,7 +1046,10 @@
             }
         }
         if (stageDir != null) {
-            mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+            try {
+                mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+            } catch (InstallerException ignored) {
+            }
         }
         if (stageCid != null) {
             PackageHelper.destroySdDir(stageCid);
diff --git a/services/core/java/com/android/server/pm/PackageManagerException.java b/services/core/java/com/android/server/pm/PackageManagerException.java
index a41636e..d04eedc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerException.java
+++ b/services/core/java/com/android/server/pm/PackageManagerException.java
@@ -16,8 +16,11 @@
 
 package com.android.server.pm;
 
+import android.content.pm.PackageManager;
 import android.content.pm.PackageParser.PackageParserException;
 
+import com.android.internal.os.InstallerConnection.InstallerException;
+
 /** {@hide} */
 public class PackageManagerException extends Exception {
     public final int error;
@@ -36,4 +39,10 @@
             throws PackageManagerException {
         throw new PackageManagerException(e.error, e.getMessage(), e.getCause());
     }
+
+    public static PackageManagerException from(InstallerException e)
+            throws PackageManagerException {
+        throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
+                e.getMessage(), e.getCause());
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 41627fd..f777faf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -219,6 +219,7 @@
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.os.IParcelFileDescriptorFactory;
+import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
@@ -2067,7 +2068,7 @@
                             }
                         } catch (FileNotFoundException e) {
                             Slog.w(TAG, "Library not found: " + lib);
-                        } catch (IOException e) {
+                        } catch (IOException | InstallerException e) {
                             Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
                                     + e.getMessage());
                         }
@@ -2136,7 +2137,11 @@
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
 
             if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
-            mInstaller.moveFiles();
+            try {
+                mInstaller.moveFiles();
+            } catch (InstallerException e) {
+                logCriticalInfo(Log.WARN, "Update commands failed: " + e);
+            }
 
             // Prune any system packages that no longer exist.
             final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
@@ -2710,11 +2715,7 @@
 
         removeDataDirsLI(ps.volumeUuid, ps.name);
         if (ps.codePath != null) {
-            if (ps.codePath.isDirectory()) {
-                mInstaller.rmPackageDir(ps.codePath.getAbsolutePath());
-            } else {
-                ps.codePath.delete();
-            }
+            removeCodePathLI(ps.codePath);
         }
         if (ps.resourcePath != null && !ps.resourcePath.equals(ps.codePath)) {
             if (ps.resourcePath.isDirectory()) {
@@ -3038,16 +3039,18 @@
         mHandler.post(new Runnable() {
             public void run() {
                 mHandler.removeCallbacks(this);
-                int retCode = -1;
+                boolean success = true;
                 synchronized (mInstallLock) {
-                    retCode = mInstaller.freeCache(volumeUuid, freeStorageSize);
-                    if (retCode < 0) {
-                        Slog.w(TAG, "Couldn't clear application caches");
+                    try {
+                        mInstaller.freeCache(volumeUuid, freeStorageSize);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Couldn't clear application caches: " + e);
+                        success = false;
                     }
                 }
                 if (observer != null) {
                     try {
-                        observer.onRemoveCompleted(null, (retCode >= 0));
+                        observer.onRemoveCompleted(null, success);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "RemoveException when invoking call back");
                     }
@@ -3065,17 +3068,19 @@
         mHandler.post(new Runnable() {
             public void run() {
                 mHandler.removeCallbacks(this);
-                int retCode = -1;
+                boolean success = true;
                 synchronized (mInstallLock) {
-                    retCode = mInstaller.freeCache(volumeUuid, freeStorageSize);
-                    if (retCode < 0) {
-                        Slog.w(TAG, "Couldn't clear application caches");
+                    try {
+                        mInstaller.freeCache(volumeUuid, freeStorageSize);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Couldn't clear application caches: " + e);
+                        success = false;
                     }
                 }
                 if(pi != null) {
                     try {
                         // Callback via pending intent
-                        int code = (retCode >= 0) ? 1 : 0;
+                        int code = success ? 1 : 0;
                         pi.sendIntent(null, code, null,
                                 null, null);
                     } catch (SendIntentException e1) {
@@ -3088,8 +3093,10 @@
 
     void freeStorage(String volumeUuid, long freeStorageSize) throws IOException {
         synchronized (mInstallLock) {
-            if (mInstaller.freeCache(volumeUuid, freeStorageSize) < 0) {
-                throw new IOException("Failed to free enough space");
+            try {
+                mInstaller.freeCache(volumeUuid, freeStorageSize);
+            } catch (InstallerException e) {
+                throw new IOException("Failed to free enough space", e);
             }
         }
     }
@@ -3121,7 +3128,7 @@
     /**
      * Update given flags based on encryption status of current user.
      */
-    private int updateFlagsForEncryption(int flags, int userId) {
+    private int updateFlags(int flags, int userId) {
         if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE
                 | PackageManager.MATCH_ENCRYPTION_AWARE)) != 0) {
             // Caller expressed an explicit opinion about what encryption
@@ -3135,6 +3142,12 @@
                 flags |= PackageManager.MATCH_ENCRYPTION_AWARE;
             }
         }
+
+        // Safe mode means we should ignore any third-party apps
+        if (mSafeMode) {
+            flags |= PackageManager.MATCH_SYSTEM_ONLY;
+        }
+
         return flags;
     }
 
@@ -3162,7 +3175,7 @@
             Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
                     + " with flags 0x" + Integer.toHexString(flags), new Throwable());
         }
-        return updateFlagsForEncryption(flags, userId);
+        return updateFlags(flags, userId);
     }
 
     /**
@@ -3194,7 +3207,7 @@
             Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
                     + " with flags 0x" + Integer.toHexString(flags), new Throwable());
         }
-        return updateFlagsForEncryption(flags, userId);
+        return updateFlags(flags, userId);
     }
 
     /**
@@ -5941,8 +5954,6 @@
                     : null;
             return ps != null
                     && mSettings.isEnabledAndMatchLPr(provider.info, flags, userId)
-                    && (!mSafeMode || (provider.info.applicationInfo.flags
-                            &ApplicationInfo.FLAG_SYSTEM) != 0)
                     ? PackageParser.generateProviderInfo(provider, flags,
                             ps.readUserState(userId), userId)
                     : null;
@@ -5997,9 +6008,7 @@
                         && (processName == null
                                 || (p.info.processName.equals(processName)
                                         && UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
-                        && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)
-                        && (!mSafeMode
-                                || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
+                        && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {
                     if (finalList == null) {
                         finalList = new ArrayList<ProviderInfo>(3);
                     }
@@ -6084,7 +6093,9 @@
         }
         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
         // TODO: generate idmap for split APKs
-        if (mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid) != 0) {
+        try {
+            mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid);
+        } catch (InstallerException e) {
             Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and "
                     + opkg.baseCodePath);
             return false;
@@ -6144,11 +6155,7 @@
                 if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                         e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
                     logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
-                    if (file.isDirectory()) {
-                        mInstaller.rmPackageDir(file.getAbsolutePath());
-                    } else {
-                        file.delete();
-                    }
+                    removeCodePathLI(file);
                 }
             }
         }
@@ -6714,50 +6721,65 @@
 
     private void createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo)
             throws PackageManagerException {
-        int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);
-        if (res != 0) {
+        // TODO: triage flags as part of 26466827
+        final int appId = UserHandle.getAppId(uid);
+        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+
+        try {
+            final int[] users = sUserManager.getUserIds();
+            for (int user : users) {
+                mInstaller.createAppData(volumeUuid, packageName, user, flags, appId, seinfo);
+            }
+        } catch (InstallerException e) {
             throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
-                    "Failed to install " + packageName + ": " + res);
+                    "Failed to prepare data directory", e);
         }
+    }
+
+    private boolean removeDataDirsLI(String volumeUuid, String packageName) {
+        // TODO: triage flags as part of 26466827
+        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+
+        boolean res = true;
+        final int[] users = sUserManager.getUserIds();
+        for (int user : users) {
+            try {
+                mInstaller.destroyAppData(volumeUuid, packageName, user, flags);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to delete data directory", e);
+                res = false;
+            }
+        }
+        return res;
+    }
+
+    void removeCodePathLI(File codePath) {
+        if (codePath.isDirectory()) {
+            try {
+                mInstaller.rmPackageDir(codePath.getAbsolutePath());
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to remove code path", e);
+            }
+        } else {
+            codePath.delete();
+        }
+    }
+
+    private void deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
+        // TODO: triage flags as part of 26466827
+        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
 
         final int[] users = sUserManager.getUserIds();
         for (int user : users) {
-            if (user != 0) {
-                res = mInstaller.createUserData(volumeUuid, packageName,
-                        UserHandle.getUid(user, uid), user, seinfo);
-                if (res != 0) {
-                    throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
-                            "Failed to createUserData " + packageName + ": " + res);
-                }
+            try {
+                mInstaller.clearAppData(volumeUuid, packageName, user,
+                        flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to delete code cache directory", e);
             }
         }
     }
 
-    private int removeDataDirsLI(String volumeUuid, String packageName) {
-        int[] users = sUserManager.getUserIds();
-        int res = 0;
-        for (int user : users) {
-            int resInner = mInstaller.remove(volumeUuid, packageName, user);
-            if (resInner < 0) {
-                res = resInner;
-            }
-        }
-
-        return res;
-    }
-
-    private int deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
-        int[] users = sUserManager.getUserIds();
-        int res = 0;
-        for (int user : users) {
-            int resInner = mInstaller.deleteCodeCacheFiles(volumeUuid, packageName, user);
-            if (resInner < 0) {
-                res = resInner;
-            }
-        }
-        return res;
-    }
-
     private void addSharedLibraryLPw(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
             PackageParser.Package changingLib) {
         if (file.path != null) {
@@ -7256,6 +7278,8 @@
             final File dataPath = Environment.getDataUserCredentialEncryptedPackageDirectory(
                     pkg.volumeUuid, UserHandle.USER_SYSTEM, pkg.packageName);
 
+            // TOOD: switch to ensure various directories
+
             boolean uidError = false;
             if (dataPath.exists()) {
                 int currentUid = 0;
@@ -7269,27 +7293,12 @@
                 // If we have mismatched owners for the data path, we have a problem.
                 if (currentUid != pkg.applicationInfo.uid) {
                     boolean recovered = false;
-                    if (currentUid == 0) {
-                        // The directory somehow became owned by root.  Wow.
-                        // This is probably because the system was stopped while
-                        // installd was in the middle of messing with its libs
-                        // directory.  Ask installd to fix that.
-                        int ret = mInstaller.fixUid(pkg.volumeUuid, pkgName,
-                                pkg.applicationInfo.uid, pkg.applicationInfo.uid);
-                        if (ret >= 0) {
-                            recovered = true;
-                            String msg = "Package " + pkg.packageName
-                                    + " unexpectedly changed to uid 0; recovered to " +
-                                    + pkg.applicationInfo.uid;
-                            reportSettingsProblem(Log.WARN, msg);
-                        }
-                    }
-                    if (!recovered && ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
-                            || (scanFlags&SCAN_BOOTING) != 0)) {
+                    if (((parseFlags & PackageParser.PARSE_IS_SYSTEM) != 0
+                            || (scanFlags & SCAN_BOOTING) != 0)) {
                         // If this is a system app, we can at least delete its
                         // current data so the application will still work.
-                        int ret = removeDataDirsLI(pkg.volumeUuid, pkgName);
-                        if (ret >= 0) {
+                        boolean res = removeDataDirsLI(pkg.volumeUuid, pkgName);
+                        if (res) {
                             // TODO: Kill the processes first
                             // Old data gone!
                             String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
@@ -7304,11 +7313,12 @@
                         if (!recovered) {
                             mHasSystemUidErrors = true;
                         }
-                    } else if (!recovered) {
+                    } else {
                         // If we allow this install to proceed, we will be broken.
                         // Abort, abort!
                         throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED,
-                                "scanPackageLI");
+                                "Expected data to be owned by UID " + pkg.applicationInfo.uid
+                                        + " but found " + currentUid);
                     }
                     if (!recovered) {
                         pkg.applicationInfo.dataDir = "/mismatched_uid/settings_"
@@ -7338,8 +7348,16 @@
 
                 if (mShouldRestoreconData) {
                     Slog.i(TAG, "SELinux relabeling of " + pkg.packageName + " issued.");
-                    mInstaller.restoreconData(pkg.volumeUuid, pkg.packageName,
-                            pkg.applicationInfo.seinfo, pkg.applicationInfo.uid);
+                    // TODO: extend this to restorecon over all users
+                    final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
+                    // TODO: triage flags as part of 26466827
+                    final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+                    try {
+                        mInstaller.restoreconAppData(pkg.volumeUuid, pkg.packageName,
+                                UserHandle.USER_SYSTEM, flags, appId, pkg.applicationInfo.seinfo);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Failed to restorecon " + pkg.packageName, e);
+                    }
                 }
             } else {
                 if (DEBUG_PACKAGE_SCANNING) {
@@ -7395,9 +7413,15 @@
             if (!TextUtils.isEmpty(pkg.volumeUuid)) {
                 for (int userId : userIds) {
                     if (userId != UserHandle.USER_SYSTEM) {
-                        mInstaller.createUserData(pkg.volumeUuid, pkg.packageName,
-                                UserHandle.getUid(userId, pkg.applicationInfo.uid), userId,
-                                pkg.applicationInfo.seinfo);
+                        // TODO: triage flags as part of 26466827
+                        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+                        final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
+                        try {
+                            mInstaller.createAppData(pkg.volumeUuid, pkg.packageName, userId,
+                                    flags, appId, pkg.applicationInfo.seinfo);
+                        } catch (InstallerException e) {
+                            throw PackageManagerException.from(e);
+                        }
                     }
                 }
             }
@@ -7411,10 +7435,11 @@
                 try {
                     final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
                     for (int userId : userIds) {
-                        if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
-                                nativeLibPath, userId) < 0) {
-                            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                                    "Failed linking native library dir (user=" + userId + ")");
+                        try {
+                            mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
+                                    nativeLibPath, userId);
+                        } catch (InstallerException e) {
+                            throw PackageManagerException.from(e);
                         }
                     }
                 } finally {
@@ -8147,8 +8172,11 @@
                     if (ps.pkg != null && ps.pkg.applicationInfo != null) {
                         ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
                         Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi);
-                        mInstaller.rmdex(ps.codePathString,
-                                getDexCodeInstructionSet(getPreferredInstructionSet()));
+                        try {
+                            mInstaller.rmdex(ps.codePathString,
+                                    getDexCodeInstructionSet(getPreferredInstructionSet()));
+                        } catch (InstallerException ignored) {
+                        }
                     }
                 }
             }
@@ -9265,10 +9293,6 @@
                 return null;
             }
             final PackageParser.Activity activity = info.activity;
-            if (mSafeMode && (activity.info.applicationInfo.flags
-                    &ApplicationInfo.FLAG_SYSTEM) == 0) {
-                return null;
-            }
             PackageSetting ps = (PackageSetting) activity.owner.mExtras;
             if (ps == null) {
                 return null;
@@ -9489,10 +9513,6 @@
                 return null;
             }
             final PackageParser.Service service = info.service;
-            if (mSafeMode && (service.info.applicationInfo.flags
-                    &ApplicationInfo.FLAG_SYSTEM) == 0) {
-                return null;
-            }
             PackageSetting ps = (PackageSetting) service.owner.mExtras;
             if (ps == null) {
                 return null;
@@ -9712,10 +9732,6 @@
                 return null;
             }
             final PackageParser.Provider provider = info.provider;
-            if (mSafeMode && (provider.info.applicationInfo.flags
-                    & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                return null;
-            }
             PackageSetting ps = (PackageSetting) provider.owner.mExtras;
             if (ps == null) {
                 return null;
@@ -11143,9 +11159,12 @@
                     final long sizeBytes = mContainerService.calculateInstalledSize(
                             origin.resolvedPath, isForwardLocked(), packageAbiOverride);
 
-                    if (mInstaller.freeCache(null, sizeBytes + lowThreshold) >= 0) {
+                    try {
+                        mInstaller.freeCache(null, sizeBytes + lowThreshold);
                         pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
                                 installFlags, packageAbiOverride);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Failed to free cache", e);
                     }
 
                     /*
@@ -11543,11 +11562,9 @@
             String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
             for (String codePath : allCodePaths) {
                 for (String dexCodeInstructionSet : dexCodeInstructionSets) {
-                    int retCode = mInstaller.rmdex(codePath, dexCodeInstructionSet);
-                    if (retCode < 0) {
-                        Slog.w(TAG, "Couldn't remove dex file for package at location " + codePath
-                                + ", retcode=" + retCode);
-                        // we don't consider this to be a failure of the core package deletion
+                    try {
+                        mInstaller.rmdex(codePath, dexCodeInstructionSet);
+                    } catch (InstallerException ignored) {
                     }
                 }
             }
@@ -11733,11 +11750,7 @@
                 return false;
             }
 
-            if (codeFile.isDirectory()) {
-                mInstaller.rmPackageDir(codeFile.getAbsolutePath());
-            } else {
-                codeFile.delete();
-            }
+            removeCodePathLI(codeFile);
 
             if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
                 resourceFile.delete();
@@ -12114,8 +12127,11 @@
             if (DEBUG_INSTALL) Slog.d(TAG, "Moving " + move.packageName + " from "
                     + move.fromUuid + " to " + move.toUuid);
             synchronized (mInstaller) {
-                if (mInstaller.copyCompleteApp(move.fromUuid, move.toUuid, move.packageName,
-                        move.dataAppName, move.appId, move.seinfo) != 0) {
+                try {
+                    mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName,
+                            move.dataAppName, move.appId, move.seinfo);
+                } catch (InstallerException e) {
+                    Slog.w(TAG, "Failed to move app", e);
                     return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
                 }
             }
@@ -12178,11 +12194,7 @@
             synchronized (mInstallLock) {
                 // Clean up both app data and code
                 removeDataDirsLI(volumeUuid, move.packageName);
-                if (codeFile.isDirectory()) {
-                    mInstaller.rmPackageDir(codeFile.getAbsolutePath());
-                } else {
-                    codeFile.delete();
-                }
+                removeCodePathLI(codeFile);
             }
             return true;
         }
@@ -13863,7 +13875,13 @@
                 outInfo.removedAppId = appId;
                 outInfo.removedUsers = new int[] {removeUser};
             }
-            mInstaller.clearUserData(ps.volumeUuid, packageName, removeUser);
+            // TODO: triage flags as part of 26466827
+            final int installerFlags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+            try {
+                mInstaller.destroyAppData(ps.volumeUuid, packageName, removeUser, installerFlags);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to delete app data", e);
+            }
             removeKeystoreDataIfNeeded(removeUser, appId);
             schedulePackageCleaning(packageName, removeUser, false);
             synchronized (mPackages) {
@@ -14037,13 +14055,16 @@
         // Always delete data directories for package, even if we found no other
         // record of app. This helps users recover from UID mismatches without
         // resorting to a full data wipe.
-        int retCode = mInstaller.clearUserData(pkg.volumeUuid, packageName, userId);
-        if (retCode < 0) {
-            Slog.w(TAG, "Couldn't remove cache files for package " + packageName);
+        // TODO: triage flags as part of 26466827
+        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        try {
+            mInstaller.clearAppData(pkg.volumeUuid, packageName, userId, flags);
+        } catch (InstallerException e) {
+            Slog.w(TAG, "Couldn't remove cache files for package " + packageName, e);
             return false;
         }
 
-        final int appId = pkg.applicationInfo.uid;
+        final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
         removeKeystoreDataIfNeeded(userId, appId);
 
         // Create a native library symlink only if we have native libraries
@@ -14052,9 +14073,11 @@
         if (pkg.applicationInfo.primaryCpuAbi != null &&
                 !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
             final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
-            if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
-                    nativeLibPath, userId) < 0) {
-                Slog.w(TAG, "Failed linking native library dir");
+            try {
+                mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
+                        nativeLibPath, userId);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed linking native library dir", e);
                 return false;
             }
         }
@@ -14267,10 +14290,14 @@
             Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
             return false;
         }
-        int retCode = mInstaller.deleteCacheFiles(p.volumeUuid, packageName, userId);
-        if (retCode < 0) {
+        // TODO: triage flags as part of 26466827
+        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        try {
+            mInstaller.clearAppData(p.volumeUuid, packageName, userId,
+                    flags | Installer.FLAG_CLEAR_CACHE_ONLY);
+        } catch (InstallerException e) {
             Slog.w(TAG, "Couldn't remove cache files for package "
-                       + packageName + " u" + userId);
+                    + packageName + " u" + userId, e);
             return false;
         }
         return true;
@@ -14364,9 +14391,12 @@
             apkPath = p.baseCodePath;
         }
 
-        int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, apkPath,
-                libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
-        if (res < 0) {
+        // TODO: triage flags as part of 26466827
+        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        try {
+            mInstaller.getAppSize(p.volumeUuid, packageName, userHandle, flags, apkPath,
+                    libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
+        } catch (InstallerException e) {
             return false;
         }
 
@@ -16581,7 +16611,11 @@
 
             if (destroyUser) {
                 synchronized (mInstallLock) {
-                    mInstaller.removeUserDataDirs(volumeUuid, userId);
+                    try {
+                        mInstaller.removeUserDataDirs(volumeUuid, userId);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Failed to clean up user dirs", e);
+                    }
                 }
             }
         }
@@ -16647,11 +16681,7 @@
                     if (packageName != null) {
                         removeDataDirsLI(volumeUuid, packageName);
                     }
-                    if (file.isDirectory()) {
-                        mInstaller.rmPackageDir(file.getAbsolutePath());
-                    } else {
-                        file.delete();
-                    }
+                    removeCodePathLI(file);
                 }
             }
         }
@@ -16987,7 +17017,11 @@
             for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
                 final String volumeUuid = vol.getFsUuid();
                 if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid);
-                mInstaller.removeUserDataDirs(volumeUuid, userHandle);
+                try {
+                    mInstaller.removeUserDataDirs(volumeUuid, userHandle);
+                } catch (InstallerException e) {
+                    Slog.w(TAG, "Failed to remove user data", e);
+                }
             }
             synchronized (mPackages) {
                 removeUnusedPackagesLILPw(userManager, userHandle);
@@ -17050,7 +17084,11 @@
     /** Called by UserManagerService */
     void createNewUser(int userHandle) {
         synchronized (mInstallLock) {
-            mInstaller.createUserConfig(userHandle);
+            try {
+                mInstaller.createUserConfig(userHandle);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to create user config", e);
+            }
             mSettings.createNewUserLI(this, mInstaller, userHandle);
         }
         synchronized (mPackages) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3f9ce7a..9fef515 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -81,6 +81,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
@@ -3668,7 +3669,7 @@
             int userHandle) {
         String[] volumeUuids;
         String[] names;
-        int[] uids;
+        int[] appIds;
         String[] seinfos;
         int packagesCount;
         synchronized (mPackages) {
@@ -3676,7 +3677,7 @@
             packagesCount = packages.size();
             volumeUuids = new String[packagesCount];
             names = new String[packagesCount];
-            uids = new int[packagesCount];
+            appIds = new int[packagesCount];
             seinfos = new String[packagesCount];
             Iterator<PackageSetting> packagesIterator = packages.iterator();
             for (int i = 0; i < packagesCount; i++) {
@@ -3690,7 +3691,7 @@
                 // required args and call the installer after mPackages lock has been released
                 volumeUuids[i] = ps.volumeUuid;
                 names[i] = ps.name;
-                uids[i] = UserHandle.getUid(userHandle, ps.appId);
+                appIds[i] = ps.appId;
                 seinfos[i] = ps.pkg.applicationInfo.seinfo;
             }
         }
@@ -3698,7 +3699,14 @@
             if (names[i] == null) {
                 continue;
             }
-            installer.createUserData(volumeUuids[i], names[i], uids[i], userHandle, seinfos[i]);
+            // TODO: triage flags!
+            final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+            try {
+                installer.createAppData(volumeUuids[i], names[i], userHandle, flags, appIds[i],
+                        seinfos[i]);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to prepare app data", e);
+            }
         }
         synchronized (mPackages) {
             applyDefaultPreferredAppsLPw(service, userHandle);
@@ -3799,63 +3807,11 @@
     }
 
     boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
-        return isEnabledLPr(componentInfo, flags, userId)
-                && isMatchLPr(componentInfo, flags);
-    }
+        final PackageSetting ps = mPackages.get(componentInfo.packageName);
+        if (ps == null) return false;
 
-    private boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) {
-        if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
-            return true;
-        }
-        final PackageSetting packageSettings = mPackages.get(componentInfo.packageName);
-        if (PackageManagerService.DEBUG_SETTINGS) {
-            Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = "
-                    + componentInfo.packageName + " componentName = " + componentInfo.name);
-            Log.v(PackageManagerService.TAG, "enabledComponents: "
-                    + compToString(packageSettings.getEnabledComponents(userId)));
-            Log.v(PackageManagerService.TAG, "disabledComponents: "
-                    + compToString(packageSettings.getDisabledComponents(userId)));
-        }
-        if (packageSettings == null) {
-            return false;
-        }
-        PackageUserState ustate = packageSettings.readUserState(userId);
-        if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) != 0) {
-            if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
-                return true;
-            }
-        }
-        if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED
-                || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
-                || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
-                || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
-                    && ustate.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
-            return false;
-        }
-        if (ustate.enabledComponents != null
-                && ustate.enabledComponents.contains(componentInfo.name)) {
-            return true;
-        }
-        if (ustate.disabledComponents != null
-                && ustate.disabledComponents.contains(componentInfo.name)) {
-            return false;
-        }
-        return componentInfo.enabled;
-    }
-
-    private boolean isMatchLPr(ComponentInfo componentInfo, int flags) {
-        if ((flags & MATCH_SYSTEM_ONLY) != 0) {
-            final PackageSetting ps = mPackages.get(componentInfo.packageName);
-            if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                return false;
-            }
-        }
-
-        final boolean matchesUnaware = ((flags & MATCH_ENCRYPTION_UNAWARE) != 0)
-                && !componentInfo.encryptionAware;
-        final boolean matchesAware = ((flags & MATCH_ENCRYPTION_AWARE) != 0)
-                && componentInfo.encryptionAware;
-        return matchesUnaware || matchesAware;
+        final PackageUserState userState = ps.readUserState(userId);
+        return userState.isMatch(componentInfo, flags);
     }
 
     String getInstallerPackageNameLPr(String packageName) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index dd58b3c..380763a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -388,6 +388,7 @@
         private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
         private static final String TAG_DISABLE_CAMERA = "disable-camera";
         private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
+        private static final String TAG_DISABLE_CONTACTS_SEARCH = "disable-contacts-search";
         private static final String TAG_DISABLE_BLUETOOTH_CONTACT_SHARING
                 = "disable-bt-contacts-sharing";
         private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";
@@ -476,6 +477,7 @@
         boolean encryptionRequested = false;
         boolean disableCamera = false;
         boolean disableCallerId = false;
+        boolean disableContactsSearch = false;
         boolean disableBluetoothContactSharing = true;
         boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
         boolean requireAutoTime = false; // Can only be set by a device owner.
@@ -638,6 +640,11 @@
                 out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId));
                 out.endTag(null, TAG_DISABLE_CALLER_ID);
             }
+            if (disableContactsSearch) {
+                out.startTag(null, TAG_DISABLE_CONTACTS_SEARCH);
+                out.attribute(null, ATTR_VALUE, Boolean.toString(disableContactsSearch));
+                out.endTag(null, TAG_DISABLE_CONTACTS_SEARCH);
+            }
             if (disableBluetoothContactSharing) {
                 out.startTag(null, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING);
                 out.attribute(null, ATTR_VALUE,
@@ -809,6 +816,9 @@
                 } else if (TAG_DISABLE_CALLER_ID.equals(tag)) {
                     disableCallerId = Boolean.parseBoolean(
                             parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_DISABLE_CONTACTS_SEARCH.equals(tag)) {
+                    disableContactsSearch = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_DISABLE_BLUETOOTH_CONTACT_SHARING.equals(tag)) {
                     disableBluetoothContactSharing = Boolean.parseBoolean(parser
                             .getAttributeValue(null, ATTR_VALUE));
@@ -1034,6 +1044,8 @@
                     pw.println(disableCamera);
             pw.print(prefix); pw.print("disableCallerId=");
                     pw.println(disableCallerId);
+            pw.print(prefix); pw.print("disableContactsSearch=");
+                    pw.println(disableContactsSearch);
             pw.print(prefix); pw.print("disableBluetoothContactSharing=");
                     pw.println(disableBluetoothContactSharing);
             pw.print(prefix); pw.print("disableScreenCapture=");
@@ -1808,7 +1820,7 @@
         if (!mHasFeature) {
             return null;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         Intent resolveIntent = new Intent();
         resolveIntent.setComponent(adminName);
         List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceiversAsUser(
@@ -2411,7 +2423,7 @@
             Bundle onEnableData) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
 
         DevicePolicyData policy = getUserData(userHandle);
         DeviceAdminInfo info = findAdmin(adminReceiver, userHandle,
@@ -2457,7 +2469,7 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null;
         }
@@ -2468,7 +2480,7 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             DevicePolicyData policyData = getUserData(userHandle);
             return policyData.mRemovingAdmins.contains(adminReceiver);
@@ -2480,7 +2492,7 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
             if (administrator == null) {
@@ -2497,7 +2509,7 @@
             return Collections.EMPTY_LIST;
         }
 
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             DevicePolicyData policy = getUserData(userHandle);
             final int N = policy.mAdminList.size();
@@ -2517,7 +2529,7 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             DevicePolicyData policy = getUserData(userHandle);
             final int N = policy.mAdminList.size();
@@ -2535,7 +2547,7 @@
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
             if (admin == null) {
@@ -2594,7 +2606,7 @@
         if (!mHasFeature) {
             return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
 
@@ -2664,7 +2676,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -2711,7 +2723,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -2771,7 +2783,7 @@
         if (!mHasFeature) {
             return 0L;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             long timeout = 0L;
 
@@ -2898,7 +2910,7 @@
         if (!mHasFeature) {
             return 0L;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             return getPasswordExpirationLocked(who, userHandle);
         }
@@ -2926,7 +2938,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -2970,7 +2982,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -3017,7 +3029,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -3067,7 +3079,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -3117,7 +3129,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -3167,7 +3179,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -3200,7 +3212,7 @@
         if (!mHasFeature) {
             return true;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
 
         synchronized (this) {
             int id = getCredentialOwner(userHandle);
@@ -3272,7 +3284,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             ActiveAdmin admin = (who != null) ? getActiveAdminUncheckedLocked(who, userHandle)
                     : getAdminWithMinimumFailedPasswordsForWipeLocked(userHandle);
@@ -3285,7 +3297,7 @@
         if (!mHasFeature) {
             return UserHandle.USER_NULL;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             ActiveAdmin admin = getAdminWithMinimumFailedPasswordsForWipeLocked(userHandle);
             return admin != null ? admin.getUserHandle().getIdentifier() : UserHandle.USER_NULL;
@@ -3584,7 +3596,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             long time = 0;
 
@@ -3906,7 +3918,7 @@
             return;
         }
         final int userHandle = mInjector.userHandleGetCallingUserId();
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             // This API can only be called by an active device admin,
             // so try to retrieve it to check that the caller is one.
@@ -3985,7 +3997,7 @@
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
 
@@ -4014,7 +4026,7 @@
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         // Managed Profile password can only be changed when per user encryption is present.
         if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) {
             enforceNotManagedProfile(userHandle, "set the active password");
@@ -4083,7 +4095,7 @@
 
     @Override
     public void reportFailedPasswordAttempt(int userHandle) {
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         enforceNotManagedProfile(userHandle, "report failed password attempt");
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -4125,7 +4137,7 @@
 
     @Override
     public void reportSuccessfulPasswordAttempt(int userHandle) {
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
 
@@ -4209,7 +4221,7 @@
         if (!mHasFeature) {
             return null;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized(this) {
             DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
             // Scan through active admins and find if anyone has already
@@ -4346,7 +4358,7 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             // Check for permissions if a particular caller is specified
             if (who != null) {
@@ -4376,7 +4388,7 @@
         if (!mHasFeature) {
             // Ok to return current status.
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         return getEncryptionStatus();
     }
 
@@ -4624,7 +4636,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         long ident = mInjector.binderClearCallingIdentity();
         try {
             synchronized (this) {
@@ -5185,17 +5197,28 @@
         }
     }
 
-    private void enforceCrossUserPermission(int userHandle) {
+    private void enforceFullCrossUsersPermission(int userHandle) {
+        enforceSystemUserOrPermission(userHandle,
+                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    }
+
+    private void enforceCrossUsersPermission(int userHandle) {
+        enforceSystemUserOrPermission(userHandle,
+                android.Manifest.permission.INTERACT_ACROSS_USERS);
+    }
+
+    private void enforceSystemUserOrPermission(int userHandle, String permission) {
         if (userHandle < 0) {
             throw new IllegalArgumentException("Invalid userId " + userHandle);
         }
         final int callingUid = mInjector.binderGetCallingUid();
-        if (userHandle == UserHandle.getUserId(callingUid)) return;
+        if (userHandle == UserHandle.getUserId(callingUid)) {
+            return;
+        }
         if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
                 || callingUid == Process.ROOT_UID)) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "Must be system or have"
-                    + " INTERACT_ACROSS_USERS_FULL permission");
+            mContext.enforceCallingOrSelfPermission(permission,
+                    "Must be system or have " + permission + " permission");
         }
     }
 
@@ -5405,7 +5428,7 @@
             return null;
         }
         Preconditions.checkNotNull(agent, "agent null");
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
 
         synchronized (this) {
             final String componentName = agent.flattenToString();
@@ -6068,7 +6091,7 @@
     @Override
     public Bundle getUserRestrictions(ComponentName who, int userHandle) {
         Preconditions.checkNotNull(who, "ComponentName is null");
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(who, userHandle);
             if (activeAdmin == null) {
@@ -6260,7 +6283,7 @@
 
     @Override
     public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
-        enforceCrossUserPermission(userId);
+        enforceFullCrossUsersPermission(userId);
         if (!mHasFeature) {
             return null;
         }
@@ -6332,7 +6355,7 @@
                     DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             if (admin.disableCallerId != disabled) {
                 admin.disableCallerId = disabled;
-                saveSettingsLocked(UserHandle.getCallingUserId());
+                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
             }
         }
     }
@@ -6352,8 +6375,7 @@
 
     @Override
     public boolean getCrossProfileCallerIdDisabledForUser(int userId) {
-        // TODO: Should there be a check to make sure this relationship is within a profile group?
-        //enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
+        enforceCrossUsersPermission(userId);
         synchronized (this) {
             ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
             return (admin != null) ? admin.disableCallerId : false;
@@ -6361,6 +6383,44 @@
     }
 
     @Override
+    public void setCrossProfileContactsSearchDisabled(ComponentName who, boolean disabled) {
+        if (!mHasFeature) {
+            return;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        synchronized (this) {
+            ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            if (admin.disableContactsSearch != disabled) {
+                admin.disableContactsSearch = disabled;
+                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+            }
+        }
+    }
+
+    @Override
+    public boolean getCrossProfileContactsSearchDisabled(ComponentName who) {
+        if (!mHasFeature) {
+            return false;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        synchronized (this) {
+            ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            return admin.disableContactsSearch;
+        }
+    }
+
+    @Override
+    public boolean getCrossProfileContactsSearchDisabledForUser(int userId) {
+        enforceCrossUsersPermission(userId);
+        synchronized (this) {
+            ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
+            return (admin != null) ? admin.disableContactsSearch : false;
+        }
+    }
+
+    @Override
     public void startManagedQuickContact(String actualLookupKey, long actualContactId,
             long actualDirectoryId, Intent originalIntent) {
         final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 2e0866c..5abb6e7 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -555,8 +555,14 @@
                         // to handle it as the change may affect ongoing print jobs.
                         UserState userState = getOrCreateUserStateLocked(getChangingUserId());
                         boolean stoppedSomePackages = false;
-                        Iterator<PrintServiceInfo> iterator = userState.getEnabledPrintServices()
-                                .iterator();
+
+                        List<PrintServiceInfo> enabledServices = userState
+                                .getEnabledPrintServices();
+                        if (enabledServices == null) {
+                            return false;
+                        }
+
+                        Iterator<PrintServiceInfo> iterator = enabledServices.iterator();
                         while (iterator.hasNext()) {
                             ComponentName componentName = iterator.next().getComponentName();
                             String componentPackage = componentName.getPackageName();
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 4198217..78edc4d 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -20,6 +20,7 @@
 import static android.content.pm.PackageManager.GET_SERVICES;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
 
+import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -336,7 +337,7 @@
         mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null);
     }
 
-    public List<PrintServiceInfo> getEnabledPrintServices() {
+    public @Nullable List<PrintServiceInfo> getEnabledPrintServices() {
         synchronized (mLock) {
             List<PrintServiceInfo> enabledServices = null;
             final int installedServiceCount = mInstalledServices.size();
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 27deb72..27d5207 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -59,6 +59,7 @@
 import android.os.MessageQueue.IdleHandler;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 import android.util.LogPrinter;
 
@@ -1504,4 +1505,10 @@
         ka3.stop();
         callback3.expectStopped();
     }
+
+    @SmallTest
+    public void testGetCaptivePortalServerUrl() throws Exception {
+        String url = mCm.getCaptivePortalServerUrl();
+        assertEquals("http://connectivitycheck.gstatic.com/generate_204", url);
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 72621a4..630367d 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -59,6 +59,7 @@
     private final Context mContext;
     private final UsageStatsDatabase mDatabase;
     private final IntervalStats[] mCurrentStats;
+    private IntervalStats mAppIdleRollingWindow;
     private boolean mStatsChanged = false;
     private final UnixCalendar mDailyExpiryDate;
     private final StatsUpdatedListener mListener;
@@ -137,6 +138,8 @@
             initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
                     mDatabase.isFirstUpdate());
         }
+
+        refreshAppIdleRollingWindow(currentTimeMillis);
     }
 
     /**
@@ -170,6 +173,7 @@
         persistActiveStats();
         mDatabase.onTimeChanged(newTime - oldTime);
         loadActiveStats(newTime, resetBeginIdleTime);
+        refreshAppIdleRollingWindow(newTime);
     }
 
     void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
@@ -211,6 +215,11 @@
             }
         }
 
+        if (event.mEventType != Event.CONFIGURATION_CHANGE) {
+            mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType);
+            mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime);
+        }
+
         notifyStatsChanged();
     }
 
@@ -222,6 +231,7 @@
         for (IntervalStats stats : mCurrentStats) {
             stats.updateBeginIdleTime(packageName, beginIdleTime);
         }
+        mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime);
         notifyStatsChanged();
     }
 
@@ -229,6 +239,7 @@
         for (IntervalStats stats : mCurrentStats) {
             stats.updateSystemLastUsedTime(packageName, lastUsedTime);
         }
+        mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime);
         notifyStatsChanged();
     }
 
@@ -387,9 +398,8 @@
     }
 
     long getBeginIdleTime(String packageName) {
-        final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY];
         UsageStats packageUsage;
-        if ((packageUsage = yearly.packageStats.get(packageName)) == null) {
+        if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
             return -1;
         } else {
             return packageUsage.getBeginIdleTime();
@@ -397,9 +407,8 @@
     }
 
     long getSystemLastUsedTime(String packageName) {
-        final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY];
         UsageStats packageUsage;
-        if ((packageUsage = yearly.packageStats.get(packageName)) == null) {
+        if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
             return -1;
         } else {
             return packageUsage.getLastTimeSystemUsed();
@@ -461,6 +470,8 @@
         }
         persistActiveStats();
 
+        refreshAppIdleRollingWindow(currentTimeMillis);
+
         final long totalTime = SystemClock.elapsedRealtime() - startTime;
         Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
                 + " milliseconds");
@@ -503,6 +514,7 @@
                 }
             }
         }
+
         mStatsChanged = false;
         updateRolloverDeadline();
     }
@@ -516,6 +528,68 @@
                 mDailyExpiryDate.getTimeInMillis() + ")");
     }
 
+    private static void mergePackageStats(IntervalStats dst, IntervalStats src) {
+        dst.endTime = Math.max(dst.endTime, src.endTime);
+
+        final int srcPackageCount = src.packageStats.size();
+        for (int i = 0; i < srcPackageCount; i++) {
+            final String packageName = src.packageStats.keyAt(i);
+            final UsageStats srcStats = src.packageStats.valueAt(i);
+            final UsageStats dstStats = dst.packageStats.get(packageName);
+            if (dstStats == null) {
+                dst.packageStats.put(packageName, new UsageStats(srcStats));
+            } else {
+                dstStats.add(src.packageStats.valueAt(i));
+            }
+        }
+    }
+
+    /**
+     * Merges all the stats into the first element of the resulting list.
+     */
+    private static final StatCombiner<IntervalStats> sPackageStatsMerger =
+            new StatCombiner<IntervalStats>() {
+        @Override
+        public void combine(IntervalStats stats, boolean mutable,
+                            List<IntervalStats> accumulatedResult) {
+            IntervalStats accum;
+            if (accumulatedResult.isEmpty()) {
+                accum = new IntervalStats();
+                accum.beginTime = stats.beginTime;
+                accumulatedResult.add(accum);
+            } else {
+                accum = accumulatedResult.get(0);
+            }
+
+            mergePackageStats(accum, stats);
+        }
+    };
+
+    /**
+     * App idle operates on a rolling window of time. When we roll over time, we end up with a
+     * period of time where in-memory stats are empty and we don't hit the disk for older stats
+     * for performance reasons. Suddenly all apps will become idle.
+     *
+     * Instead, at times we do a deep query to find all the apps that have run in the past few
+     * days and keep the cached data up to date.
+     *
+     * @param currentTimeMillis
+     */
+    void refreshAppIdleRollingWindow(long currentTimeMillis) {
+        // Start the rolling window for AppIdle requests.
+        List<IntervalStats> stats = mDatabase.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
+                currentTimeMillis - (1000 * 60 * 60 * 24 * 2), currentTimeMillis,
+                sPackageStatsMerger);
+
+        if (stats == null || stats.isEmpty()) {
+            mAppIdleRollingWindow = new IntervalStats();
+            mergePackageStats(mAppIdleRollingWindow,
+                    mCurrentStats[UsageStatsManager.INTERVAL_YEARLY]);
+        } else {
+            mAppIdleRollingWindow = stats.get(0);
+        }
+    }
+
     //
     // -- DUMP related methods --
     //
@@ -538,6 +612,9 @@
             pw.println(" stats");
             printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true);
         }
+
+        pw.println("AppIdleRollingWindow cache");
+        printIntervalStats(pw, mAppIdleRollingWindow, screenOnTime, true);
     }
 
     private String formatDateTime(long dateTime, boolean pretty) {
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index b18af33..b56ce73 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -687,7 +687,7 @@
                 .append("] PhoneAccount: ")
                 .append(mAccountHandle)
                 .append(" Capabilities: ")
-                .append(mCapabilities)
+                .append(capabilitiesToString(mCapabilities))
                 .append(" Schemes: ");
         for (String scheme : mSupportedUriSchemes) {
             sb.append(scheme)
@@ -698,4 +698,42 @@
         sb.append("]");
         return sb.toString();
     }
+
+    /**
+     * Generates a string representation of a capabilities bitmask.
+     *
+     * @param capabilities The capabilities bitmask.
+     * @return String representation of the capabilities bitmask.
+     */
+    private String capabilitiesToString(int capabilities) {
+        StringBuilder sb = new StringBuilder();
+        if (hasCapabilities(CAPABILITY_VIDEO_CALLING)) {
+            sb.append("Video ");
+        }
+        if (hasCapabilities(CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)) {
+            sb.append("Presence ");
+        }
+        if (hasCapabilities(CAPABILITY_CALL_PROVIDER)) {
+            sb.append("CallProvider ");
+        }
+        if (hasCapabilities(CAPABILITY_CALL_SUBJECT)) {
+            sb.append("CallSubject ");
+        }
+        if (hasCapabilities(CAPABILITY_CONNECTION_MANAGER)) {
+            sb.append("ConnectionMgr ");
+        }
+        if (hasCapabilities(CAPABILITY_EMERGENCY_CALLS_ONLY)) {
+            sb.append("EmergOnly ");
+        }
+        if (hasCapabilities(CAPABILITY_MULTI_USER)) {
+            sb.append("MultiUser ");
+        }
+        if (hasCapabilities(CAPABILITY_PLACE_EMERGENCY_CALLS)) {
+            sb.append("PlaceEmerg ");
+        }
+        if (hasCapabilities(CAPABILITY_SIM_SUBSCRIPTION)) {
+            sb.append("SimSub ");
+        }
+        return sb.toString();
+    }
 }
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index dabf706..216603c 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -16,13 +16,23 @@
 
 package android.telecom;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Represents attributes of video calls.
  */
 public class VideoProfile implements Parcelable {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({QUALITY_UNKNOWN, QUALITY_HIGH, QUALITY_MEDIUM, QUALITY_LOW, QUALITY_DEFAULT})
+    public @interface VideoQuality {}
+
     /**
      * "Unknown" video quality.
      * @hide
@@ -48,6 +58,14 @@
      */
     public static final int QUALITY_DEFAULT = 4;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            flag = true,
+            value = {STATE_AUDIO_ONLY, STATE_TX_ENABLED, STATE_RX_ENABLED, STATE_BIDIRECTIONAL,
+                    STATE_PAUSED})
+    public @interface VideoState {}
+
     /**
      * Used when answering or dialing a call to indicate that the call does not have a video
      * component.
@@ -107,7 +125,7 @@
      *
      * @param videoState The video state.
      */
-    public VideoProfile(int videoState) {
+    public VideoProfile(@VideoState int videoState) {
         this(videoState, QUALITY_DEFAULT);
     }
 
@@ -117,7 +135,7 @@
      * @param videoState The video state.
      * @param quality The video quality.
      */
-    public VideoProfile(int videoState, int quality) {
+    public VideoProfile(@VideoState int videoState, @VideoQuality int quality) {
         mVideoState = videoState;
         mQuality = quality;
     }
@@ -130,6 +148,7 @@
      * {@link VideoProfile#STATE_RX_ENABLED},
      * {@link VideoProfile#STATE_PAUSED}.
      */
+    @VideoState
     public int getVideoState() {
         return mVideoState;
     }
@@ -139,6 +158,7 @@
      * Valid values: {@link VideoProfile#QUALITY_HIGH}, {@link VideoProfile#QUALITY_MEDIUM},
      * {@link VideoProfile#QUALITY_LOW}, {@link VideoProfile#QUALITY_DEFAULT}.
      */
+    @VideoQuality
     public int getQuality() {
         return mQuality;
     }
@@ -211,7 +231,7 @@
      * @param videoState The video state.
      * @return String representation of the video state.
      */
-    public static String videoStateToString(int videoState) {
+    public static String videoStateToString(@VideoState int videoState) {
         StringBuilder sb = new StringBuilder();
         sb.append("Audio");
 
@@ -240,7 +260,7 @@
      * @param videoState The video state.
      * @return {@code True} if the video state is audio only, {@code false} otherwise.
      */
-    public static boolean isAudioOnly(int videoState) {
+    public static boolean isAudioOnly(@VideoState int videoState) {
         return !hasState(videoState, VideoProfile.STATE_TX_ENABLED)
                 && !hasState(videoState, VideoProfile.STATE_RX_ENABLED);
     }
@@ -251,7 +271,7 @@
      * @param videoState The video state.
      * @return {@code True} if video transmission or reception is enabled, {@code false} otherwise.
      */
-    public static boolean isVideo(int videoState) {
+    public static boolean isVideo(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_TX_ENABLED)
                 || hasState(videoState, VideoProfile.STATE_RX_ENABLED)
                 || hasState(videoState, VideoProfile.STATE_BIDIRECTIONAL);
@@ -263,7 +283,7 @@
      * @param videoState The video state.
      * @return {@code True} if video transmission is enabled, {@code false} otherwise.
      */
-    public static boolean isTransmissionEnabled(int videoState) {
+    public static boolean isTransmissionEnabled(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_TX_ENABLED);
     }
 
@@ -273,7 +293,7 @@
      * @param videoState The video state.
      * @return {@code True} if video reception is enabled, {@code false} otherwise.
      */
-    public static boolean isReceptionEnabled(int videoState) {
+    public static boolean isReceptionEnabled(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_RX_ENABLED);
     }
 
@@ -283,7 +303,7 @@
      * @param videoState The video state.
      * @return {@code True} if the video is bi-directional, {@code false} otherwise.
      */
-    public static boolean isBidirectional(int videoState) {
+    public static boolean isBidirectional(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_BIDIRECTIONAL);
     }
 
@@ -293,7 +313,7 @@
      * @param videoState The video state.
      * @return {@code True} if the video is paused, {@code false} otherwise.
      */
-    public static boolean isPaused(int videoState) {
+    public static boolean isPaused(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_PAUSED);
     }
 
@@ -304,7 +324,7 @@
      * @param state The state to check.
      * @return {@code True} if the state is set.
      */
-    private static boolean hasState(int videoState, int state) {
+    private static boolean hasState(@VideoState int videoState, @VideoState int state) {
         return (videoState & state) == state;
     }
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6ffc026..80c5b1e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -473,6 +473,15 @@
     public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
 
     /**
+     * Determine whether user can switch Wi-Fi preferred or Cellular preferred in calling preference.
+     * Some operators support Wi-Fi Calling only, not VoLTE.
+     * They don't need "Cellular preferred" option.
+     * In this case, set uneditalbe attribute for preferred preference.
+     * @hide
+     */
+    public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
+
+    /**
      * If this is true, the SIM card (through Customer Service Profile EF file) will be able to
      * prevent manual operator selection. If false, this SIM setting will be ignored and manual
      * operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more
@@ -553,6 +562,23 @@
     public static final String BOOL_ALLOW_VIDEO_PAUSE =
             "bool_allow_video_pause";
 
+
+    /**
+     * Flag indicating whether the carrier supports RCS presence indication for video calls.  When
+     * {@code true}, the carrier supports RCS presence indication for video calls.  When presence
+     * is supported, the device should use the
+     * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the
+     * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate
+     * whether each contact supports video calling.  The UI is made aware that presence is enabled
+     * via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE}
+     * and can choose to hide or show the video calling icon based on whether a contact supports
+     * video.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -628,6 +654,7 @@
         sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
         sDefaults.putBoolean(BOOL_ALLOW_EMERGENCY_VIDEO_CALLS, false);
         sDefaults.putBoolean(BOOL_ALLOW_VIDEO_PAUSE, true);
+        sDefaults.putBoolean(KEY_EDITABLE_WFC_MODE_BOOL, true);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
@@ -662,6 +689,7 @@
         sDefaults.putString(KEY_MMS_UA_PROF_URL_STRING, "");
         sDefaults.putString(KEY_MMS_USER_AGENT_STRING, "");
         sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true);
+        sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false);
     }
 
     /**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 9998937..39f3213 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -24,6 +24,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.net.Uri;
+import android.os.Looper;
 import android.telephony.Rlog;
 import android.os.Handler;
 import android.os.Message;
@@ -347,7 +348,31 @@
      * for #onSubscriptionsChanged to be invoked.
      */
     public static class OnSubscriptionsChangedListener {
-        private final Handler mHandler  = new Handler() {
+        private final Handler mHandler;
+
+        public OnSubscriptionsChangedListener() {
+            mHandler = new OnSubscriptionsChangedListenerHandler();
+        }
+
+        /**
+         * Contructor that takes in looper as parameter in case a subclass/instantiation needs
+         * to use a specific looper (like in tests where mainLooper may need to be used).
+         * @param looper Looper to be used for mHandler
+         * @hide
+         */
+        protected OnSubscriptionsChangedListener(Looper looper) {
+            mHandler = new OnSubscriptionsChangedListenerHandler(looper);
+        }
+
+        private class OnSubscriptionsChangedListenerHandler extends Handler {
+            private OnSubscriptionsChangedListenerHandler() {
+                super();
+            }
+
+            private OnSubscriptionsChangedListenerHandler(Looper looper) {
+                super(looper);
+            }
+
             @Override
             public void handleMessage(Message msg) {
                 if (DBG) {
@@ -355,7 +380,7 @@
                 }
                 OnSubscriptionsChangedListener.this.onSubscriptionsChanged();
             }
-        };
+        }
 
         /**
          * Callback invoked when there is any change to any SubscriptionInfo. Typically