Merge "Skip ANR for processes that have been killed"
diff --git a/Android.mk b/Android.mk
index 69e57b6..3b99111 100644
--- a/Android.mk
+++ b/Android.mk
@@ -125,6 +125,7 @@
 	core/java/android/bluetooth/IBluetoothGatt.aidl \
 	core/java/android/bluetooth/IBluetoothGattCallback.aidl \
 	core/java/android/bluetooth/IBluetoothGattServerCallback.aidl \
+	core/java/android/bluetooth/le/IAdvertiserCallback.aidl \
 	core/java/android/content/IClipboard.aidl \
 	core/java/android/content/IContentService.aidl \
 	core/java/android/content/IIntentReceiver.aidl \
@@ -484,6 +485,12 @@
 			$(framework_res_source_path)/android/Manifest.java \
 			$(framework_res_source_path)/com/android/internal/R.java
 
+# Make sure that R.java and Manifest.java are built before we build
+# the source for this library.
+framework_res_R_stamp := \
+	$(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp
+LOCAL_ADDITIONAL_DEPENDENCIES := $(framework_res_R_stamp)
+
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVA_LIBRARIES := core-oj core-libart core-lambda-stubs conscrypt okhttp core-junit bouncycastle ext
 LOCAL_STATIC_JAVA_LIBRARIES := framework-protos
@@ -495,15 +502,8 @@
 LOCAL_RMTYPEDEFS := true
 
 include $(BUILD_JAVA_LIBRARY)
+
 framework_module := $(LOCAL_INSTALLED_MODULE)
-
-# Make sure that R.java and Manifest.java are built before we build
-# the source for this library.
-framework_res_R_stamp := \
-	$(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp
-$(full_classes_compiled_jar): $(framework_res_R_stamp)
-$(built_dex_intermediate): $(framework_res_R_stamp)
-
 $(framework_module): | $(dir $(framework_module))framework-res.apk
 
 framework_built := $(call java-lib-deps,framework)
diff --git a/api/current.txt b/api/current.txt
index 5a7c100..e779e9b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35314,6 +35314,7 @@
     method public static java.net.SocketAddress getsockname(java.io.FileDescriptor) throws android.system.ErrnoException;
     method public static int gettid();
     method public static int getuid();
+    method public static byte[] getxattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static java.lang.String if_indextoname(int);
     method public static int if_nametoindex(java.lang.String);
     method public static java.net.InetAddress inet_pton(int, java.lang.String);
@@ -35322,6 +35323,7 @@
     method public static void lchown(java.lang.String, int, int) throws android.system.ErrnoException;
     method public static void link(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void listen(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+    method public static java.lang.String[] listxattr(java.lang.String) throws android.system.ErrnoException;
     method public static long lseek(java.io.FileDescriptor, long, int) throws android.system.ErrnoException;
     method public static android.system.StructStat lstat(java.lang.String) throws android.system.ErrnoException;
     method public static void mincore(long, long, byte[]) throws android.system.ErrnoException;
@@ -35348,6 +35350,7 @@
     method public static int recvfrom(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method public static int recvfrom(java.io.FileDescriptor, byte[], int, int, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method public static void remove(java.lang.String) throws android.system.ErrnoException;
+    method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
     method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
@@ -35359,6 +35362,7 @@
     method public static int setsid() throws android.system.ErrnoException;
     method public static void setsockoptInt(java.io.FileDescriptor, int, int, int) throws android.system.ErrnoException;
     method public static void setuid(int) throws android.system.ErrnoException;
+    method public static void setxattr(java.lang.String, java.lang.String, byte[], int) throws android.system.ErrnoException;
     method public static void shutdown(java.io.FileDescriptor, int) throws android.system.ErrnoException;
     method public static java.io.FileDescriptor socket(int, int, int) throws android.system.ErrnoException;
     method public static void socketpair(int, int, int, java.io.FileDescriptor, java.io.FileDescriptor) throws android.system.ErrnoException;
@@ -50501,6 +50505,7 @@
     method public boolean isPrimitive();
     method public boolean isSynthetic();
     method public T newInstance() throws java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public java.lang.String toGenericString();
   }
 
   public class ClassCastException extends java.lang.RuntimeException {
@@ -51998,6 +52003,7 @@
     method public static boolean isTransient(int);
     method public static boolean isVolatile(int);
     method public static int methodModifiers();
+    method public static int parameterModifiers();
     method public static java.lang.String toString(int);
     field public static final int ABSTRACT = 1024; // 0x400
     field public static final int FINAL = 16; // 0x10
@@ -54775,6 +54781,13 @@
     method public abstract java.security.ProtectionDomain[] combine(java.security.ProtectionDomain[], java.security.ProtectionDomain[]);
   }
 
+  public final class DomainLoadStoreParameter implements java.security.KeyStore.LoadStoreParameter {
+    ctor public DomainLoadStoreParameter(java.net.URI, java.util.Map<java.lang.String, java.security.KeyStore.ProtectionParameter>);
+    method public java.net.URI getConfiguration();
+    method public java.security.KeyStore.ProtectionParameter getProtectionParameter();
+    method public java.util.Map<java.lang.String, java.security.KeyStore.ProtectionParameter> getProtectionParams();
+  }
+
   public class GeneralSecurityException extends java.lang.Exception {
     ctor public GeneralSecurityException();
     ctor public GeneralSecurityException(java.lang.String);
@@ -54971,6 +54984,12 @@
   }
 
   public static abstract interface KeyStore.Entry {
+    method public default java.util.Set<java.security.KeyStore.Entry.Attribute> getAttributes();
+  }
+
+  public static abstract interface KeyStore.Entry.Attribute {
+    method public abstract java.lang.String getName();
+    method public abstract java.lang.String getValue();
   }
 
   public static abstract interface KeyStore.LoadStoreParameter {
@@ -54979,11 +54998,15 @@
 
   public static class KeyStore.PasswordProtection implements javax.security.auth.Destroyable java.security.KeyStore.ProtectionParameter {
     ctor public KeyStore.PasswordProtection(char[]);
+    ctor public KeyStore.PasswordProtection(char[], java.lang.String, java.security.spec.AlgorithmParameterSpec);
     method public synchronized char[] getPassword();
+    method public java.lang.String getProtectionAlgorithm();
+    method public java.security.spec.AlgorithmParameterSpec getProtectionParameters();
   }
 
   public static final class KeyStore.PrivateKeyEntry implements java.security.KeyStore.Entry {
     ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[]);
+    ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[], java.util.Set<java.security.KeyStore.Entry.Attribute>);
     method public java.security.cert.Certificate getCertificate();
     method public java.security.cert.Certificate[] getCertificateChain();
     method public java.security.PrivateKey getPrivateKey();
@@ -54994,11 +55017,13 @@
 
   public static final class KeyStore.SecretKeyEntry implements java.security.KeyStore.Entry {
     ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey);
+    ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey, java.util.Set<java.security.KeyStore.Entry.Attribute>);
     method public javax.crypto.SecretKey getSecretKey();
   }
 
   public static final class KeyStore.TrustedCertificateEntry implements java.security.KeyStore.Entry {
     ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate);
+    ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate, java.util.Set<java.security.KeyStore.Entry.Attribute>);
     method public java.security.cert.Certificate getTrustedCertificate();
   }
 
@@ -55077,6 +55102,14 @@
     ctor public NoSuchProviderException(java.lang.String);
   }
 
+  public final class PKCS12Attribute implements java.security.KeyStore.Entry.Attribute {
+    ctor public PKCS12Attribute(java.lang.String, java.lang.String);
+    ctor public PKCS12Attribute(byte[]);
+    method public byte[] getEncoded();
+    method public java.lang.String getName();
+    method public java.lang.String getValue();
+  }
+
   public abstract class Permission implements java.security.Guard java.io.Serializable {
     ctor public Permission(java.lang.String);
     method public void checkGuard(java.lang.Object) throws java.lang.SecurityException;
@@ -55134,10 +55167,11 @@
     method public abstract boolean equals(java.lang.Object);
     method public abstract java.lang.String getName();
     method public abstract int hashCode();
+    method public default boolean implies(javax.security.auth.Subject);
     method public abstract java.lang.String toString();
   }
 
-  public abstract interface PrivateKey implements java.security.Key {
+  public abstract interface PrivateKey implements javax.security.auth.Destroyable java.security.Key {
     field public static final long serialVersionUID = 6034044314589513430L; // 0x53bd3b559a12c6d6L
   }
 
@@ -55166,16 +55200,25 @@
 
   public abstract class Provider extends java.util.Properties {
     ctor protected Provider(java.lang.String, double, java.lang.String);
+    method public synchronized java.lang.Object compute(java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>);
+    method public synchronized java.lang.Object computeIfAbsent(java.lang.Object, java.util.function.Function<? super java.lang.Object, ? extends java.lang.Object>);
+    method public synchronized java.lang.Object computeIfPresent(java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>);
     method public synchronized void forEach(java.util.function.BiConsumer<? super java.lang.Object, ? super java.lang.Object>);
     method public java.lang.String getInfo();
     method public java.lang.String getName();
+    method public synchronized java.lang.Object getOrDefault(java.lang.Object, java.lang.Object);
     method public synchronized java.security.Provider.Service getService(java.lang.String, java.lang.String);
     method public synchronized java.util.Set<java.security.Provider.Service> getServices();
     method public double getVersion();
+    method public synchronized java.lang.Object merge(java.lang.Object, java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>);
     method public synchronized java.lang.Object put(java.lang.Object, java.lang.Object);
     method public synchronized void putAll(java.util.Map<?, ?>);
+    method public synchronized java.lang.Object putIfAbsent(java.lang.Object, java.lang.Object);
     method protected synchronized void putService(java.security.Provider.Service);
     method protected synchronized void removeService(java.security.Provider.Service);
+    method public synchronized boolean replace(java.lang.Object, java.lang.Object, java.lang.Object);
+    method public synchronized java.lang.Object replace(java.lang.Object, java.lang.Object);
+    method public synchronized void replaceAll(java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>);
   }
 
   public static class Provider.Service {
@@ -55217,6 +55260,7 @@
     method public static java.security.SecureRandom getInstance(java.lang.String) throws java.security.NoSuchAlgorithmException;
     method public static java.security.SecureRandom getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.SecureRandom getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.SecureRandom getInstanceStrong() throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
     method public static byte[] getSeed(int);
     method protected final int next(int);
@@ -55835,6 +55879,7 @@
     method public abstract int getVersion();
     method public abstract void verify(java.security.PublicKey) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
     method public abstract void verify(java.security.PublicKey, java.lang.String) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
+    method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
   }
 
   public abstract class X509CRLEntry implements java.security.cert.X509Extension {
@@ -58282,6 +58327,33 @@
     method public static java.lang.String toString(java.lang.Object[]);
   }
 
+  public class Base64 {
+    method public static java.util.Base64.Decoder getDecoder();
+    method public static java.util.Base64.Encoder getEncoder();
+    method public static java.util.Base64.Decoder getMimeDecoder();
+    method public static java.util.Base64.Encoder getMimeEncoder();
+    method public static java.util.Base64.Encoder getMimeEncoder(int, byte[]);
+    method public static java.util.Base64.Decoder getUrlDecoder();
+    method public static java.util.Base64.Encoder getUrlEncoder();
+  }
+
+  public static class Base64.Decoder {
+    method public byte[] decode(byte[]);
+    method public byte[] decode(java.lang.String);
+    method public int decode(byte[], byte[]);
+    method public java.nio.ByteBuffer decode(java.nio.ByteBuffer);
+    method public java.io.InputStream wrap(java.io.InputStream);
+  }
+
+  public static class Base64.Encoder {
+    method public byte[] encode(byte[]);
+    method public int encode(byte[], byte[]);
+    method public java.nio.ByteBuffer encode(java.nio.ByteBuffer);
+    method public java.lang.String encodeToString(byte[]);
+    method public java.util.Base64.Encoder withoutPadding();
+    method public java.io.OutputStream wrap(java.io.OutputStream);
+  }
+
   public class BitSet implements java.lang.Cloneable java.io.Serializable {
     ctor public BitSet();
     ctor public BitSet(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index f57851c..a4477f3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -38130,6 +38130,7 @@
     method public static java.net.SocketAddress getsockname(java.io.FileDescriptor) throws android.system.ErrnoException;
     method public static int gettid();
     method public static int getuid();
+    method public static byte[] getxattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static java.lang.String if_indextoname(int);
     method public static int if_nametoindex(java.lang.String);
     method public static java.net.InetAddress inet_pton(int, java.lang.String);
@@ -38138,6 +38139,7 @@
     method public static void lchown(java.lang.String, int, int) throws android.system.ErrnoException;
     method public static void link(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void listen(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+    method public static java.lang.String[] listxattr(java.lang.String) throws android.system.ErrnoException;
     method public static long lseek(java.io.FileDescriptor, long, int) throws android.system.ErrnoException;
     method public static android.system.StructStat lstat(java.lang.String) throws android.system.ErrnoException;
     method public static void mincore(long, long, byte[]) throws android.system.ErrnoException;
@@ -38164,6 +38166,7 @@
     method public static int recvfrom(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method public static int recvfrom(java.io.FileDescriptor, byte[], int, int, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method public static void remove(java.lang.String) throws android.system.ErrnoException;
+    method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
     method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
@@ -38175,6 +38178,7 @@
     method public static int setsid() throws android.system.ErrnoException;
     method public static void setsockoptInt(java.io.FileDescriptor, int, int, int) throws android.system.ErrnoException;
     method public static void setuid(int) throws android.system.ErrnoException;
+    method public static void setxattr(java.lang.String, java.lang.String, byte[], int) throws android.system.ErrnoException;
     method public static void shutdown(java.io.FileDescriptor, int) throws android.system.ErrnoException;
     method public static java.io.FileDescriptor socket(int, int, int) throws android.system.ErrnoException;
     method public static void socketpair(int, int, int, java.io.FileDescriptor, java.io.FileDescriptor) throws android.system.ErrnoException;
@@ -53857,6 +53861,7 @@
     method public boolean isPrimitive();
     method public boolean isSynthetic();
     method public T newInstance() throws java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public java.lang.String toGenericString();
   }
 
   public class ClassCastException extends java.lang.RuntimeException {
@@ -55354,6 +55359,7 @@
     method public static boolean isTransient(int);
     method public static boolean isVolatile(int);
     method public static int methodModifiers();
+    method public static int parameterModifiers();
     method public static java.lang.String toString(int);
     field public static final int ABSTRACT = 1024; // 0x400
     field public static final int FINAL = 16; // 0x10
@@ -58131,6 +58137,13 @@
     method public abstract java.security.ProtectionDomain[] combine(java.security.ProtectionDomain[], java.security.ProtectionDomain[]);
   }
 
+  public final class DomainLoadStoreParameter implements java.security.KeyStore.LoadStoreParameter {
+    ctor public DomainLoadStoreParameter(java.net.URI, java.util.Map<java.lang.String, java.security.KeyStore.ProtectionParameter>);
+    method public java.net.URI getConfiguration();
+    method public java.security.KeyStore.ProtectionParameter getProtectionParameter();
+    method public java.util.Map<java.lang.String, java.security.KeyStore.ProtectionParameter> getProtectionParams();
+  }
+
   public class GeneralSecurityException extends java.lang.Exception {
     ctor public GeneralSecurityException();
     ctor public GeneralSecurityException(java.lang.String);
@@ -58327,6 +58340,12 @@
   }
 
   public static abstract interface KeyStore.Entry {
+    method public default java.util.Set<java.security.KeyStore.Entry.Attribute> getAttributes();
+  }
+
+  public static abstract interface KeyStore.Entry.Attribute {
+    method public abstract java.lang.String getName();
+    method public abstract java.lang.String getValue();
   }
 
   public static abstract interface KeyStore.LoadStoreParameter {
@@ -58335,11 +58354,15 @@
 
   public static class KeyStore.PasswordProtection implements javax.security.auth.Destroyable java.security.KeyStore.ProtectionParameter {
     ctor public KeyStore.PasswordProtection(char[]);
+    ctor public KeyStore.PasswordProtection(char[], java.lang.String, java.security.spec.AlgorithmParameterSpec);
     method public synchronized char[] getPassword();
+    method public java.lang.String getProtectionAlgorithm();
+    method public java.security.spec.AlgorithmParameterSpec getProtectionParameters();
   }
 
   public static final class KeyStore.PrivateKeyEntry implements java.security.KeyStore.Entry {
     ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[]);
+    ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[], java.util.Set<java.security.KeyStore.Entry.Attribute>);
     method public java.security.cert.Certificate getCertificate();
     method public java.security.cert.Certificate[] getCertificateChain();
     method public java.security.PrivateKey getPrivateKey();
@@ -58350,11 +58373,13 @@
 
   public static final class KeyStore.SecretKeyEntry implements java.security.KeyStore.Entry {
     ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey);
+    ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey, java.util.Set<java.security.KeyStore.Entry.Attribute>);
     method public javax.crypto.SecretKey getSecretKey();
   }
 
   public static final class KeyStore.TrustedCertificateEntry implements java.security.KeyStore.Entry {
     ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate);
+    ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate, java.util.Set<java.security.KeyStore.Entry.Attribute>);
     method public java.security.cert.Certificate getTrustedCertificate();
   }
 
@@ -58433,6 +58458,14 @@
     ctor public NoSuchProviderException(java.lang.String);
   }
 
+  public final class PKCS12Attribute implements java.security.KeyStore.Entry.Attribute {
+    ctor public PKCS12Attribute(java.lang.String, java.lang.String);
+    ctor public PKCS12Attribute(byte[]);
+    method public byte[] getEncoded();
+    method public java.lang.String getName();
+    method public java.lang.String getValue();
+  }
+
   public abstract class Permission implements java.security.Guard java.io.Serializable {
     ctor public Permission(java.lang.String);
     method public void checkGuard(java.lang.Object) throws java.lang.SecurityException;
@@ -58490,10 +58523,11 @@
     method public abstract boolean equals(java.lang.Object);
     method public abstract java.lang.String getName();
     method public abstract int hashCode();
+    method public default boolean implies(javax.security.auth.Subject);
     method public abstract java.lang.String toString();
   }
 
-  public abstract interface PrivateKey implements java.security.Key {
+  public abstract interface PrivateKey implements javax.security.auth.Destroyable java.security.Key {
     field public static final long serialVersionUID = 6034044314589513430L; // 0x53bd3b559a12c6d6L
   }
 
@@ -58522,16 +58556,25 @@
 
   public abstract class Provider extends java.util.Properties {
     ctor protected Provider(java.lang.String, double, java.lang.String);
+    method public synchronized java.lang.Object compute(java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>);
+    method public synchronized java.lang.Object computeIfAbsent(java.lang.Object, java.util.function.Function<? super java.lang.Object, ? extends java.lang.Object>);
+    method public synchronized java.lang.Object computeIfPresent(java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>);
     method public synchronized void forEach(java.util.function.BiConsumer<? super java.lang.Object, ? super java.lang.Object>);
     method public java.lang.String getInfo();
     method public java.lang.String getName();
+    method public synchronized java.lang.Object getOrDefault(java.lang.Object, java.lang.Object);
     method public synchronized java.security.Provider.Service getService(java.lang.String, java.lang.String);
     method public synchronized java.util.Set<java.security.Provider.Service> getServices();
     method public double getVersion();
+    method public synchronized java.lang.Object merge(java.lang.Object, java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>);
     method public synchronized java.lang.Object put(java.lang.Object, java.lang.Object);
     method public synchronized void putAll(java.util.Map<?, ?>);
+    method public synchronized java.lang.Object putIfAbsent(java.lang.Object, java.lang.Object);
     method protected synchronized void putService(java.security.Provider.Service);
     method protected synchronized void removeService(java.security.Provider.Service);
+    method public synchronized boolean replace(java.lang.Object, java.lang.Object, java.lang.Object);
+    method public synchronized java.lang.Object replace(java.lang.Object, java.lang.Object);
+    method public synchronized void replaceAll(java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>);
   }
 
   public static class Provider.Service {
@@ -58573,6 +58616,7 @@
     method public static java.security.SecureRandom getInstance(java.lang.String) throws java.security.NoSuchAlgorithmException;
     method public static java.security.SecureRandom getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.SecureRandom getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.SecureRandom getInstanceStrong() throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
     method public static byte[] getSeed(int);
     method protected final int next(int);
@@ -59191,6 +59235,7 @@
     method public abstract int getVersion();
     method public abstract void verify(java.security.PublicKey) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
     method public abstract void verify(java.security.PublicKey, java.lang.String) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
+    method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
   }
 
   public abstract class X509CRLEntry implements java.security.cert.X509Extension {
@@ -61638,6 +61683,33 @@
     method public static java.lang.String toString(java.lang.Object[]);
   }
 
+  public class Base64 {
+    method public static java.util.Base64.Decoder getDecoder();
+    method public static java.util.Base64.Encoder getEncoder();
+    method public static java.util.Base64.Decoder getMimeDecoder();
+    method public static java.util.Base64.Encoder getMimeEncoder();
+    method public static java.util.Base64.Encoder getMimeEncoder(int, byte[]);
+    method public static java.util.Base64.Decoder getUrlDecoder();
+    method public static java.util.Base64.Encoder getUrlEncoder();
+  }
+
+  public static class Base64.Decoder {
+    method public byte[] decode(byte[]);
+    method public byte[] decode(java.lang.String);
+    method public int decode(byte[], byte[]);
+    method public java.nio.ByteBuffer decode(java.nio.ByteBuffer);
+    method public java.io.InputStream wrap(java.io.InputStream);
+  }
+
+  public static class Base64.Encoder {
+    method public byte[] encode(byte[]);
+    method public int encode(byte[], byte[]);
+    method public java.nio.ByteBuffer encode(java.nio.ByteBuffer);
+    method public java.lang.String encodeToString(byte[]);
+    method public java.util.Base64.Encoder withoutPadding();
+    method public java.io.OutputStream wrap(java.io.OutputStream);
+  }
+
   public class BitSet implements java.lang.Cloneable java.io.Serializable {
     ctor public BitSet();
     ctor public BitSet(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 3ac9757..b3572ba 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -35391,6 +35391,7 @@
     method public static java.net.SocketAddress getsockname(java.io.FileDescriptor) throws android.system.ErrnoException;
     method public static int gettid();
     method public static int getuid();
+    method public static byte[] getxattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static java.lang.String if_indextoname(int);
     method public static int if_nametoindex(java.lang.String);
     method public static java.net.InetAddress inet_pton(int, java.lang.String);
@@ -35399,6 +35400,7 @@
     method public static void lchown(java.lang.String, int, int) throws android.system.ErrnoException;
     method public static void link(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void listen(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+    method public static java.lang.String[] listxattr(java.lang.String) throws android.system.ErrnoException;
     method public static long lseek(java.io.FileDescriptor, long, int) throws android.system.ErrnoException;
     method public static android.system.StructStat lstat(java.lang.String) throws android.system.ErrnoException;
     method public static void mincore(long, long, byte[]) throws android.system.ErrnoException;
@@ -35425,6 +35427,7 @@
     method public static int recvfrom(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method public static int recvfrom(java.io.FileDescriptor, byte[], int, int, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method public static void remove(java.lang.String) throws android.system.ErrnoException;
+    method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
     method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
@@ -35436,6 +35439,7 @@
     method public static int setsid() throws android.system.ErrnoException;
     method public static void setsockoptInt(java.io.FileDescriptor, int, int, int) throws android.system.ErrnoException;
     method public static void setuid(int) throws android.system.ErrnoException;
+    method public static void setxattr(java.lang.String, java.lang.String, byte[], int) throws android.system.ErrnoException;
     method public static void shutdown(java.io.FileDescriptor, int) throws android.system.ErrnoException;
     method public static java.io.FileDescriptor socket(int, int, int) throws android.system.ErrnoException;
     method public static void socketpair(int, int, int, java.io.FileDescriptor, java.io.FileDescriptor) throws android.system.ErrnoException;
@@ -50581,6 +50585,7 @@
     method public boolean isPrimitive();
     method public boolean isSynthetic();
     method public T newInstance() throws java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public java.lang.String toGenericString();
   }
 
   public class ClassCastException extends java.lang.RuntimeException {
@@ -52078,6 +52083,7 @@
     method public static boolean isTransient(int);
     method public static boolean isVolatile(int);
     method public static int methodModifiers();
+    method public static int parameterModifiers();
     method public static java.lang.String toString(int);
     field public static final int ABSTRACT = 1024; // 0x400
     field public static final int FINAL = 16; // 0x10
@@ -54855,6 +54861,13 @@
     method public abstract java.security.ProtectionDomain[] combine(java.security.ProtectionDomain[], java.security.ProtectionDomain[]);
   }
 
+  public final class DomainLoadStoreParameter implements java.security.KeyStore.LoadStoreParameter {
+    ctor public DomainLoadStoreParameter(java.net.URI, java.util.Map<java.lang.String, java.security.KeyStore.ProtectionParameter>);
+    method public java.net.URI getConfiguration();
+    method public java.security.KeyStore.ProtectionParameter getProtectionParameter();
+    method public java.util.Map<java.lang.String, java.security.KeyStore.ProtectionParameter> getProtectionParams();
+  }
+
   public class GeneralSecurityException extends java.lang.Exception {
     ctor public GeneralSecurityException();
     ctor public GeneralSecurityException(java.lang.String);
@@ -55051,6 +55064,12 @@
   }
 
   public static abstract interface KeyStore.Entry {
+    method public default java.util.Set<java.security.KeyStore.Entry.Attribute> getAttributes();
+  }
+
+  public static abstract interface KeyStore.Entry.Attribute {
+    method public abstract java.lang.String getName();
+    method public abstract java.lang.String getValue();
   }
 
   public static abstract interface KeyStore.LoadStoreParameter {
@@ -55059,11 +55078,15 @@
 
   public static class KeyStore.PasswordProtection implements javax.security.auth.Destroyable java.security.KeyStore.ProtectionParameter {
     ctor public KeyStore.PasswordProtection(char[]);
+    ctor public KeyStore.PasswordProtection(char[], java.lang.String, java.security.spec.AlgorithmParameterSpec);
     method public synchronized char[] getPassword();
+    method public java.lang.String getProtectionAlgorithm();
+    method public java.security.spec.AlgorithmParameterSpec getProtectionParameters();
   }
 
   public static final class KeyStore.PrivateKeyEntry implements java.security.KeyStore.Entry {
     ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[]);
+    ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[], java.util.Set<java.security.KeyStore.Entry.Attribute>);
     method public java.security.cert.Certificate getCertificate();
     method public java.security.cert.Certificate[] getCertificateChain();
     method public java.security.PrivateKey getPrivateKey();
@@ -55074,11 +55097,13 @@
 
   public static final class KeyStore.SecretKeyEntry implements java.security.KeyStore.Entry {
     ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey);
+    ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey, java.util.Set<java.security.KeyStore.Entry.Attribute>);
     method public javax.crypto.SecretKey getSecretKey();
   }
 
   public static final class KeyStore.TrustedCertificateEntry implements java.security.KeyStore.Entry {
     ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate);
+    ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate, java.util.Set<java.security.KeyStore.Entry.Attribute>);
     method public java.security.cert.Certificate getTrustedCertificate();
   }
 
@@ -55157,6 +55182,14 @@
     ctor public NoSuchProviderException(java.lang.String);
   }
 
+  public final class PKCS12Attribute implements java.security.KeyStore.Entry.Attribute {
+    ctor public PKCS12Attribute(java.lang.String, java.lang.String);
+    ctor public PKCS12Attribute(byte[]);
+    method public byte[] getEncoded();
+    method public java.lang.String getName();
+    method public java.lang.String getValue();
+  }
+
   public abstract class Permission implements java.security.Guard java.io.Serializable {
     ctor public Permission(java.lang.String);
     method public void checkGuard(java.lang.Object) throws java.lang.SecurityException;
@@ -55214,10 +55247,11 @@
     method public abstract boolean equals(java.lang.Object);
     method public abstract java.lang.String getName();
     method public abstract int hashCode();
+    method public default boolean implies(javax.security.auth.Subject);
     method public abstract java.lang.String toString();
   }
 
-  public abstract interface PrivateKey implements java.security.Key {
+  public abstract interface PrivateKey implements javax.security.auth.Destroyable java.security.Key {
     field public static final long serialVersionUID = 6034044314589513430L; // 0x53bd3b559a12c6d6L
   }
 
@@ -55246,16 +55280,25 @@
 
   public abstract class Provider extends java.util.Properties {
     ctor protected Provider(java.lang.String, double, java.lang.String);
+    method public synchronized java.lang.Object compute(java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>);
+    method public synchronized java.lang.Object computeIfAbsent(java.lang.Object, java.util.function.Function<? super java.lang.Object, ? extends java.lang.Object>);
+    method public synchronized java.lang.Object computeIfPresent(java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>);
     method public synchronized void forEach(java.util.function.BiConsumer<? super java.lang.Object, ? super java.lang.Object>);
     method public java.lang.String getInfo();
     method public java.lang.String getName();
+    method public synchronized java.lang.Object getOrDefault(java.lang.Object, java.lang.Object);
     method public synchronized java.security.Provider.Service getService(java.lang.String, java.lang.String);
     method public synchronized java.util.Set<java.security.Provider.Service> getServices();
     method public double getVersion();
+    method public synchronized java.lang.Object merge(java.lang.Object, java.lang.Object, java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>);
     method public synchronized java.lang.Object put(java.lang.Object, java.lang.Object);
     method public synchronized void putAll(java.util.Map<?, ?>);
+    method public synchronized java.lang.Object putIfAbsent(java.lang.Object, java.lang.Object);
     method protected synchronized void putService(java.security.Provider.Service);
     method protected synchronized void removeService(java.security.Provider.Service);
+    method public synchronized boolean replace(java.lang.Object, java.lang.Object, java.lang.Object);
+    method public synchronized java.lang.Object replace(java.lang.Object, java.lang.Object);
+    method public synchronized void replaceAll(java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>);
   }
 
   public static class Provider.Service {
@@ -55297,6 +55340,7 @@
     method public static java.security.SecureRandom getInstance(java.lang.String) throws java.security.NoSuchAlgorithmException;
     method public static java.security.SecureRandom getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.SecureRandom getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.SecureRandom getInstanceStrong() throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
     method public static byte[] getSeed(int);
     method protected final int next(int);
@@ -55915,6 +55959,7 @@
     method public abstract int getVersion();
     method public abstract void verify(java.security.PublicKey) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
     method public abstract void verify(java.security.PublicKey, java.lang.String) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
+    method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
   }
 
   public abstract class X509CRLEntry implements java.security.cert.X509Extension {
@@ -58362,6 +58407,33 @@
     method public static java.lang.String toString(java.lang.Object[]);
   }
 
+  public class Base64 {
+    method public static java.util.Base64.Decoder getDecoder();
+    method public static java.util.Base64.Encoder getEncoder();
+    method public static java.util.Base64.Decoder getMimeDecoder();
+    method public static java.util.Base64.Encoder getMimeEncoder();
+    method public static java.util.Base64.Encoder getMimeEncoder(int, byte[]);
+    method public static java.util.Base64.Decoder getUrlDecoder();
+    method public static java.util.Base64.Encoder getUrlEncoder();
+  }
+
+  public static class Base64.Decoder {
+    method public byte[] decode(byte[]);
+    method public byte[] decode(java.lang.String);
+    method public int decode(byte[], byte[]);
+    method public java.nio.ByteBuffer decode(java.nio.ByteBuffer);
+    method public java.io.InputStream wrap(java.io.InputStream);
+  }
+
+  public static class Base64.Encoder {
+    method public byte[] encode(byte[]);
+    method public int encode(byte[], byte[]);
+    method public java.nio.ByteBuffer encode(java.nio.ByteBuffer);
+    method public java.lang.String encodeToString(byte[]);
+    method public java.util.Base64.Encoder withoutPadding();
+    method public java.io.OutputStream wrap(java.io.OutputStream);
+  }
+
   public class BitSet implements java.lang.Cloneable java.io.Serializable {
     ctor public BitSet();
     ctor public BitSet(int);
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 80af5ea..18ad43e 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -185,12 +185,7 @@
 int main(int argc, char* const argv[])
 {
     if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
-        // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return
-        // EINVAL. Don't die on such kernels.
-        if (errno != EINVAL) {
-            LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
-            return 12;
-        }
+        LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
     }
 
     AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
@@ -309,6 +304,5 @@
         fprintf(stderr, "Error: no class name or --zygote supplied.\n");
         app_usage();
         LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
-        return 10;
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
index 17e533a..da81569 100644
--- a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
+++ b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
@@ -83,11 +83,6 @@
     }
 
     @Override
-    public void onMultiAdvertiseCallback(int status, boolean isStart,
-            AdvertiseSettings advertiseSettings) throws RemoteException {
-    }
-
-    @Override
     public void onConfigureMTU(String address, int mtu, int status) throws RemoteException {
     }
 
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 7498038..f4ebcaf 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -28,6 +28,7 @@
 
 import android.bluetooth.IBluetoothGattCallback;
 import android.bluetooth.IBluetoothGattServerCallback;
+import android.bluetooth.le.IAdvertiserCallback;
 
 /**
  * API for interacting with BLE / GATT
@@ -41,11 +42,15 @@
                    in String callingPackage);
     void stopScan(in int appIf, in boolean isServer);
     void flushPendingBatchResults(in int appIf, in boolean isServer);
-    void startMultiAdvertising(in int appIf,
+
+    void registerAdvertiser(in IAdvertiserCallback callback);
+    void unregisterAdvertiser(in int advertiserId);
+    void startMultiAdvertising(in int advertiserId,
                                in AdvertiseData advertiseData,
                                in AdvertiseData scanResponse,
                                in AdvertiseSettings settings);
-    void stopMultiAdvertising(in int appIf);
+    void stopMultiAdvertising(in int advertiserId);
+
     void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback);
     void unregisterClient(in int clientIf);
     void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport);
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index 7163c37..efda08e 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -38,8 +38,6 @@
     void onDescriptorWrite(in String address, in int status, in int handle);
     void onNotify(in String address, in int handle, in byte[] value);
     void onReadRemoteRssi(in String address, in int rssi, in int status);
-    void onMultiAdvertiseCallback(in int status, boolean isStart,
-                                  in AdvertiseSettings advertiseSettings);
     void onScanManagerErrorCallback(in int errorCode);
     void onConfigureMTU(in String address, in int mtu, in int status);
     void onFoundOrLost(in boolean onFound, in ScanResult scanResult);
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index d468bd4..048f791 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -22,6 +22,7 @@
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.IBluetoothGatt;
 import android.bluetooth.IBluetoothManager;
+import android.bluetooth.le.IAdvertiserCallback;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelUuid;
@@ -162,7 +163,7 @@
     }
 
     /**
-     * Cleans up advertise clients. Should be called when bluetooth is down.
+     * Cleans up advertisers. Should be called when bluetooth is down.
      *
      * @hide
      */
@@ -228,7 +229,7 @@
     /**
      * Bluetooth GATT interface callbacks for advertising.
      */
-    private class AdvertiseCallbackWrapper extends BluetoothGattCallbackWrapper {
+    private class AdvertiseCallbackWrapper extends IAdvertiserCallback.Stub {
         private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000;
         private final AdvertiseCallback mAdvertiseCallback;
         private final AdvertiseData mAdvertisement;
@@ -236,10 +237,10 @@
         private final AdvertiseSettings mSettings;
         private final IBluetoothGatt mBluetoothGatt;
 
-        // mClientIf 0: not registered
+        // mAdvertiserId 0: not registered
         // -1: advertise stopped or registration timeout
         // >0: registered and advertising started
-        private int mClientIf;
+        private int mAdvertiserId;
         private boolean mIsAdvertising = false;
 
         public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,
@@ -251,35 +252,34 @@
             mScanResponse = scanResponse;
             mSettings = settings;
             mBluetoothGatt = bluetoothGatt;
-            mClientIf = 0;
+            mAdvertiserId = 0;
         }
 
         public void startRegisteration() {
             synchronized (this) {
-                if (mClientIf == -1) return;
+                if (mAdvertiserId == -1) return;
 
                 try {
-                    UUID uuid = UUID.randomUUID();
-                    mBluetoothGatt.registerClient(new ParcelUuid(uuid), this);
+                    mBluetoothGatt.registerAdvertiser(this);
                     wait(LE_CALLBACK_TIMEOUT_MILLIS);
                 } catch (InterruptedException | RemoteException e) {
                     Log.e(TAG, "Failed to start registeration", e);
                 }
-                if (mClientIf > 0 && mIsAdvertising) {
+                if (mAdvertiserId > 0 && mIsAdvertising) {
                     mLeAdvertisers.put(mAdvertiseCallback, this);
-                } else if (mClientIf <= 0) {
+                } else if (mAdvertiserId <= 0) {
 
                     // Registration timeout, reset mClientIf to -1 so no subsequent operations can
                     // proceed.
-                    if (mClientIf == 0) mClientIf = -1;
+                    if (mAdvertiserId == 0) mAdvertiserId = -1;
                     // Post internal error if registration failed.
                     postStartFailure(mAdvertiseCallback,
                             AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
                 } else {
                     // Unregister application if it's already registered but advertise failed.
                     try {
-                        mBluetoothGatt.unregisterClient(mClientIf);
-                        mClientIf = -1;
+                        mBluetoothGatt.unregisterAdvertiser(mAdvertiserId);
+                        mAdvertiserId = -1;
                     } catch (RemoteException e) {
                         Log.e(TAG, "remote exception when unregistering", e);
                     }
@@ -290,7 +290,7 @@
         public void stopAdvertising() {
             synchronized (this) {
                 try {
-                    mBluetoothGatt.stopMultiAdvertising(mClientIf);
+                    mBluetoothGatt.stopMultiAdvertising(mAdvertiserId);
                     wait(LE_CALLBACK_TIMEOUT_MILLIS);
                 } catch (InterruptedException | RemoteException e) {
                     Log.e(TAG, "Failed to stop advertising", e);
@@ -305,20 +305,20 @@
         }
 
         /**
-         * Application interface registered - app is ready to go
+         * Advertiser interface registered - app is ready to go
          */
         @Override
-        public void onClientRegistered(int status, int clientIf) {
-            Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf);
+        public void onAdvertiserRegistered(int status, int advertiserId) {
+            Log.d(TAG, "onAdvertiserRegistered() - status=" + status + " advertiserId=" + advertiserId);
             synchronized (this) {
                 if (status == BluetoothGatt.GATT_SUCCESS) {
                     try {
-                        if (mClientIf == -1) {
-                            // Registration succeeds after timeout, unregister client.
-                            mBluetoothGatt.unregisterClient(clientIf);
+                        if (mAdvertiserId == -1) {
+                            // Registration succeeds after timeout, unregister advertiser.
+                            mBluetoothGatt.unregisterAdvertiser(advertiserId);
                         } else {
-                            mClientIf = clientIf;
-                            mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement,
+                            mAdvertiserId = advertiserId;
+                            mBluetoothGatt.startMultiAdvertising(mAdvertiserId, mAdvertisement,
                                     mScanResponse, mSettings);
                         }
                         return;
@@ -327,7 +327,7 @@
                     }
                 }
                 // Registration failed.
-                mClientIf = -1;
+                mAdvertiserId = -1;
                 notifyAll();
             }
         }
@@ -346,10 +346,10 @@
                         postStartFailure(mAdvertiseCallback, status);
                     }
                 } else {
-                    // unregister client for stop.
+                    // unregister advertiser for stop.
                     try {
-                        mBluetoothGatt.unregisterClient(mClientIf);
-                        mClientIf = -1;
+                        mBluetoothGatt.unregisterAdvertiser(mAdvertiserId);
+                        mAdvertiserId = -1;
                         mIsAdvertising = false;
                         mLeAdvertisers.remove(mAdvertiseCallback);
                     } catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/le/IAdvertiserCallback.aidl b/core/java/android/bluetooth/le/IAdvertiserCallback.aidl
new file mode 100644
index 0000000..c58b1df
--- /dev/null
+++ b/core/java/android/bluetooth/le/IAdvertiserCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.bluetooth.le;
+
+import android.bluetooth.le.AdvertiseSettings;
+
+/**
+ * Callback definitions for interacting with Advertiser
+ * @hide
+ */
+oneway interface IAdvertiserCallback {
+    void onAdvertiserRegistered(in int status, in int advertiserId);
+
+    void onMultiAdvertiseCallback(in int status, boolean isStart,
+                                  in AdvertiseSettings advertiseSettings);
+}
diff --git a/core/java/android/content/pm/IOtaDexopt.aidl b/core/java/android/content/pm/IOtaDexopt.aidl
index 8f38d6f..467bd5f 100644
--- a/core/java/android/content/pm/IOtaDexopt.aidl
+++ b/core/java/android/content/pm/IOtaDexopt.aidl
@@ -42,8 +42,21 @@
     boolean isDone();
 
     /**
+     * Return the progress (0..1) made in this session. When {@link #isDone() isDone} returns
+     * true, the progress value will be 1.
+     */
+    float getProgress();
+
+    /**
      * Optimize the next package. Note: this command is synchronous, that is, only returns after
      * the package has been dexopted (or dexopting failed).
+     *
+     * Note: this will be removed after a transition period. Use nextDexoptCommand instead.
      */
     void dexoptNextPackage();
+
+    /**
+     * Get the optimization parameters for the next package.
+     */
+    String nextDexoptCommand();
 }
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 6162d1a..693228f 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -347,15 +347,17 @@
     @VisibleForTesting
     protected boolean inSystemImage(int callerUid) {
         String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
-        for (String name : packages) {
-            try {
-                PackageInfo packageInfo =
-                        mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
-                if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                    return true;
+        if (packages != null) {
+            for (String name : packages) {
+                try {
+                    PackageInfo packageInfo =
+                            mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
+                    if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                        return true;
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    return false;
                 }
-            } catch (PackageManager.NameNotFoundException e) {
-                return false;
             }
         }
         return false;
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index b83fb26..0f0e9c4 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -516,13 +516,11 @@
                     Os.setsockoptLinger(fd, OsConstants.SOL_SOCKET, OsConstants.SO_LINGER, linger);
                     break;
                 case SocketOptions.SO_TIMEOUT:
-                    /*
-                     * SO_TIMEOUT from the core library gets converted to
-                     * SO_SNDTIMEO, but the option is supposed to set both
-                     * send and receive timeouts. Note: The incoming timeout
-                     * value is in milliseconds.
-                     */
+                    // The option must set both send and receive timeouts.
+                    // Note: The incoming timeout value is in milliseconds.
                     StructTimeval timeval = StructTimeval.fromMillis(intValue);
+                    Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO,
+                            timeval);
                     Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO,
                             timeval);
                     break;
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 6243f46..ebb9601 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -405,8 +405,15 @@
      */
     public static final int TRANSPORT_VPN = 4;
 
+    /**
+     * Indicates this network uses a Wi-Fi NAN transport.
+     *
+     * @hide PROPOSED_NAN_API
+     */
+    public static final int TRANSPORT_WIFI_NAN = 5;
+
     private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
-    private static final int MAX_TRANSPORT = TRANSPORT_VPN;
+    private static final int MAX_TRANSPORT = TRANSPORT_WIFI_NAN;
 
     /**
      * Adds the given transport type to this {@code NetworkCapability} instance.
@@ -862,6 +869,7 @@
                 case TRANSPORT_BLUETOOTH:   transports += "BLUETOOTH"; break;
                 case TRANSPORT_ETHERNET:    transports += "ETHERNET"; break;
                 case TRANSPORT_VPN:         transports += "VPN"; break;
+                case TRANSPORT_WIFI_NAN:    transports += "WIFI_NAN"; break;
             }
             if (++i < types.length) transports += "|";
         }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index d8be2b6..93fc13c 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -286,6 +286,11 @@
     }
 
     /** {@hide} */
+    public static File getReferenceProfile(String packageName) {
+        return buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName);
+    }
+
+    /** {@hide} */
     public static File getDataProfilesDePackageDirectory(int userId, String packageName) {
         return buildPath(getDataProfilesDeDirectory(userId), packageName);
     }
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
new file mode 100644
index 0000000..5ff79f7
--- /dev/null
+++ b/core/java/android/os/HwBinder.java
@@ -0,0 +1,59 @@
+/*
+ * 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.os;
+
+import libcore.util.NativeAllocationRegistry;
+
+/** @hide */
+public abstract class HwBinder implements IHwBinder {
+    private static final String TAG = "HwBinder";
+
+    private static final NativeAllocationRegistry sNativeRegistry;
+
+    public HwBinder() {
+        native_setup();
+
+        sNativeRegistry.registerNativeAllocation(
+                this,
+                mNativeContext);
+    }
+
+    public final native void transact(
+            int code, HwParcel request, HwParcel reply, int flags);
+
+    public abstract void onTransact(
+            int code, HwParcel request, HwParcel reply, int flags);
+
+    public native final void registerService(String serviceName);
+    public static native final IHwBinder getService(String serviceName);
+
+    // Returns address of the "freeFunction".
+    private static native final long native_init();
+
+    private native final void native_setup();
+
+    static {
+        long freeFunction = native_init();
+
+        sNativeRegistry = new NativeAllocationRegistry(
+                HwBinder.class.getClassLoader(),
+                freeFunction,
+                128 /* size */);
+    }
+
+    private long mNativeContext;
+}
diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java
new file mode 100644
index 0000000..153c6e6
--- /dev/null
+++ b/core/java/android/os/HwBlob.java
@@ -0,0 +1,74 @@
+/*
+ * 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.os;
+
+import libcore.util.NativeAllocationRegistry;
+
+/** @hide */
+public class HwBlob {
+    private static final String TAG = "HwBlob";
+
+    private static final NativeAllocationRegistry sNativeRegistry;
+
+    public HwBlob(int size) {
+        native_setup(size);
+
+        sNativeRegistry.registerNativeAllocation(
+                this,
+                mNativeContext);
+    }
+
+    public native final boolean getBool(long offset);
+    public native final byte getInt8(long offset);
+    public native final short getInt16(long offset);
+    public native final int getInt32(long offset);
+    public native final long getInt64(long offset);
+    public native final float getFloat(long offset);
+    public native final double getDouble(long offset);
+    public native final String getString(long offset);
+
+    public native final void putBool(long offset, boolean x);
+    public native final void putInt8(long offset, byte x);
+    public native final void putInt16(long offset, short x);
+    public native final void putInt32(long offset, int x);
+    public native final void putInt64(long offset, long x);
+    public native final void putFloat(long offset, float x);
+    public native final void putDouble(long offset, double x);
+    public native final void putString(long offset, String x);
+
+    public native final void putBlob(long offset, HwBlob blob);
+
+    public native final long handle();
+
+    // Returns address of the "freeFunction".
+    private static native final long native_init();
+
+    private native final void native_setup(int size);
+
+    static {
+        long freeFunction = native_init();
+
+        sNativeRegistry = new NativeAllocationRegistry(
+                HwBlob.class.getClassLoader(),
+                freeFunction,
+                128 /* size */);
+    }
+
+    private long mNativeContext;
+}
+
+
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
new file mode 100644
index 0000000..e4d5718
--- /dev/null
+++ b/core/java/android/os/HwParcel.java
@@ -0,0 +1,134 @@
+/*
+ * 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.os;
+
+import libcore.util.NativeAllocationRegistry;
+
+/** @hide */
+public class HwParcel {
+    private static final String TAG = "HwParcel";
+
+    public static final int STATUS_SUCCESS      = 0;
+    public static final int STATUS_ERROR        = -1;
+
+    private static final NativeAllocationRegistry sNativeRegistry;
+
+    private HwParcel(boolean allocate) {
+        native_setup(allocate);
+
+        sNativeRegistry.registerNativeAllocation(
+                this,
+                mNativeContext);
+    }
+
+    public HwParcel() {
+        native_setup(true /* allocate */);
+
+        sNativeRegistry.registerNativeAllocation(
+                this,
+                mNativeContext);
+    }
+
+    public native final void writeInterfaceToken(String interfaceName);
+    public native final void writeBool(boolean val);
+    public native final void writeInt8(byte val);
+    public native final void writeInt16(short val);
+    public native final void writeInt32(int val);
+    public native final void writeInt64(long val);
+    public native final void writeFloat(float val);
+    public native final void writeDouble(double val);
+    public native final void writeString(String val);
+
+    public native final void writeBoolArray(int size, boolean[] val);
+    public native final void writeBoolVector(boolean[] val);
+    public native final void writeInt8Array(int size, byte[] val);
+    public native final void writeInt8Vector(byte[] val);
+    public native final void writeInt16Array(int size, short[] val);
+    public native final void writeInt16Vector(short[] val);
+    public native final void writeInt32Array(int size, int[] val);
+    public native final void writeInt32Vector(int[] val);
+    public native final void writeInt64Array(int size, long[] val);
+    public native final void writeInt64Vector(long[] val);
+    public native final void writeFloatArray(int size, float[] val);
+    public native final void writeFloatVector(float[] val);
+    public native final void writeDoubleArray(int size, double[] val);
+    public native final void writeDoubleVector(double[] val);
+    public native final void writeStringArray(int size, String[] val);
+    public native final void writeStringVector(String[] val);
+
+    public native final void writeStrongBinder(IHwBinder binder);
+
+    public native final void enforceInterface(String interfaceName);
+    public native final boolean readBool();
+    public native final byte readInt8();
+    public native final short readInt16();
+    public native final int readInt32();
+    public native final long readInt64();
+    public native final float readFloat();
+    public native final double readDouble();
+    public native final String readString();
+
+    public native final boolean[] readBoolArray(int size);
+    public native final boolean[] readBoolVector();
+    public native final byte[] readInt8Array(int size);
+    public native final byte[] readInt8Vector();
+    public native final short[] readInt16Array(int size);
+    public native final short[] readInt16Vector();
+    public native final int[] readInt32Array(int size);
+    public native final int[] readInt32Vector();
+    public native final long[] readInt64Array(int size);
+    public native final long[] readInt64Vector();
+    public native final float[] readFloatArray(int size);
+    public native final float[] readFloatVector();
+    public native final double[] readDoubleArray(int size);
+    public native final double[] readDoubleVector();
+    public native final String[] readStringArray(int size);
+    public native final String[] readStringVector();
+
+    public native final IHwBinder readStrongBinder();
+
+    // Handle is stored as part of the blob.
+    public native final HwBlob readBuffer();
+
+    public native final HwBlob readEmbeddedBuffer(
+            long parentHandle, long offset);
+
+    public native final void writeBuffer(HwBlob blob);
+
+    public native final void writeStatus(int status);
+    public native final void verifySuccess();
+    public native final void releaseTemporaryStorage();
+
+    public native final void send();
+
+    // Returns address of the "freeFunction".
+    private static native final long native_init();
+
+    private native final void native_setup(boolean allocate);
+
+    static {
+        long freeFunction = native_init();
+
+        sNativeRegistry = new NativeAllocationRegistry(
+                HwParcel.class.getClassLoader(),
+                freeFunction,
+                128 /* size */);
+    }
+
+    private long mNativeContext;
+}
+
diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java
new file mode 100644
index 0000000..83866b3
--- /dev/null
+++ b/core/java/android/os/HwRemoteBinder.java
@@ -0,0 +1,56 @@
+/*
+ * 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.os;
+
+import libcore.util.NativeAllocationRegistry;
+
+/** @hide */
+public class HwRemoteBinder implements IHwBinder {
+    private static final String TAG = "HwRemoteBinder";
+
+    private static final NativeAllocationRegistry sNativeRegistry;
+
+    public HwRemoteBinder() {
+        native_setup_empty();
+
+        sNativeRegistry.registerNativeAllocation(
+                this,
+                mNativeContext);
+    }
+
+    public IHwInterface queryLocalInterface(String descriptor) {
+        return null;
+    }
+
+    public native final void transact(
+            int code, HwParcel request, HwParcel reply, int flags);
+
+    private static native final long native_init();
+
+    private native final void native_setup_empty();
+
+    static {
+        long freeFunction = native_init();
+
+        sNativeRegistry = new NativeAllocationRegistry(
+                HwRemoteBinder.class.getClassLoader(),
+                freeFunction,
+                128 /* size */);
+    }
+
+    private long mNativeContext;
+}
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
new file mode 100644
index 0000000..76e881e
--- /dev/null
+++ b/core/java/android/os/IHwBinder.java
@@ -0,0 +1,29 @@
+/*
+ * 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.os;
+
+/** @hide */
+public interface IHwBinder {
+    // These MUST match their corresponding libhwbinder/IBinder.h definition !!!
+    public static final int FIRST_CALL_TRANSACTION = 1;
+    public static final int FLAG_ONEWAY = 1;
+
+    public void transact(
+            int code, HwParcel request, HwParcel reply, int flags);
+
+    public IHwInterface queryLocalInterface(String descriptor);
+}
diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java
new file mode 100644
index 0000000..7c5ac6f
--- /dev/null
+++ b/core/java/android/os/IHwInterface.java
@@ -0,0 +1,22 @@
+/*
+ * 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.os;
+
+/** @hide */
+public interface IHwInterface {
+    public IHwBinder asBinder();
+}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index f664e70..6aa9fac 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -16,34 +16,9 @@
 
 package android.os;
 
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
 import android.system.Os;
 import android.util.Log;
-import com.android.internal.os.Zygote;
 import dalvik.system.VMRuntime;
-import java.io.BufferedWriter;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/*package*/ class ZygoteStartFailedEx extends Exception {
-    ZygoteStartFailedEx(String s) {
-        super(s);
-    }
-
-    ZygoteStartFailedEx(Throwable cause) {
-        super(cause);
-    }
-
-    ZygoteStartFailedEx(String s, Throwable cause) {
-        super(s, cause);
-    }
-}
 
 /**
  * Tools for managing OS processes.
@@ -387,83 +362,11 @@
     private static long sStartUptimeMillis;
 
     /**
-     * State for communicating with the zygote process.
-     *
-     * @hide for internal use only.
+     * State associated with the zygote process.
+     * @hide
      */
-    public static class ZygoteState {
-        final LocalSocket socket;
-        final DataInputStream inputStream;
-        final BufferedWriter writer;
-        final List<String> abiList;
-
-        boolean mClosed;
-
-        private ZygoteState(LocalSocket socket, DataInputStream inputStream,
-                BufferedWriter writer, List<String> abiList) {
-            this.socket = socket;
-            this.inputStream = inputStream;
-            this.writer = writer;
-            this.abiList = abiList;
-        }
-
-        public static ZygoteState connect(String socketAddress) throws IOException {
-            DataInputStream zygoteInputStream = null;
-            BufferedWriter zygoteWriter = null;
-            final LocalSocket zygoteSocket = new LocalSocket();
-
-            try {
-                zygoteSocket.connect(new LocalSocketAddress(socketAddress,
-                        LocalSocketAddress.Namespace.RESERVED));
-
-                zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
-
-                zygoteWriter = new BufferedWriter(new OutputStreamWriter(
-                        zygoteSocket.getOutputStream()), 256);
-            } catch (IOException ex) {
-                try {
-                    zygoteSocket.close();
-                } catch (IOException ignore) {
-                }
-
-                throw ex;
-            }
-
-            String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
-            Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);
-
-            return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
-                    Arrays.asList(abiListString.split(",")));
-        }
-
-        boolean matches(String abi) {
-            return abiList.contains(abi);
-        }
-
-        public void close() {
-            try {
-                socket.close();
-            } catch (IOException ex) {
-                Log.e(LOG_TAG,"I/O exception on routine close", ex);
-            }
-
-            mClosed = true;
-        }
-
-        boolean isClosed() {
-            return mClosed;
-        }
-    }
-
-    /**
-     * The state of the connection to the primary zygote.
-     */
-    static ZygoteState primaryZygoteState;
-
-    /**
-     * The state of the connection to the secondary zygote.
-     */
-    static ZygoteState secondaryZygoteState;
+    public static final ZygoteProcess zygoteProcess =
+            new ZygoteProcess(ZYGOTE_SOCKET, SECONDARY_ZYGOTE_SOCKET);
 
     /**
      * Start a new process.
@@ -509,263 +412,9 @@
                                   String instructionSet,
                                   String appDataDir,
                                   String[] zygoteArgs) {
-        try {
-            return startViaZygote(processClass, niceName, uid, gid, gids,
+        return zygoteProcess.start(processClass, niceName, uid, gid, gids,
                     debugFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, zygoteArgs);
-        } catch (ZygoteStartFailedEx ex) {
-            Log.e(LOG_TAG,
-                    "Starting VM process through Zygote failed");
-            throw new RuntimeException(
-                    "Starting VM process through Zygote failed", ex);
-        }
-    }
-
-    /** retry interval for opening a zygote socket */
-    static final int ZYGOTE_RETRY_MILLIS = 500;
-
-    /**
-     * Queries the zygote for the list of ABIS it supports.
-     *
-     * @throws ZygoteStartFailedEx if the query failed.
-     */
-    private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
-            throws IOException {
-        // Each query starts with the argument count (1 in this case)
-        writer.write("1");
-        // ... followed by a new-line.
-        writer.newLine();
-        // ... followed by our only argument.
-        writer.write("--query-abi-list");
-        writer.newLine();
-        writer.flush();
-
-        // The response is a length prefixed stream of ASCII bytes.
-        int numBytes = inputStream.readInt();
-        byte[] bytes = new byte[numBytes];
-        inputStream.readFully(bytes);
-
-        return new String(bytes, StandardCharsets.US_ASCII);
-    }
-
-    /**
-     * Sends an argument list to the zygote process, which starts a new child
-     * and returns the child's pid. Please note: the present implementation
-     * replaces newlines in the argument list with spaces.
-     *
-     * @throws ZygoteStartFailedEx if process start failed for any reason
-     */
-    private static ProcessStartResult zygoteSendArgsAndGetResult(
-            ZygoteState zygoteState, ArrayList<String> args)
-            throws ZygoteStartFailedEx {
-        try {
-            /**
-             * See com.android.internal.os.ZygoteInit.readArgumentList()
-             * Presently the wire format to the zygote process is:
-             * a) a count of arguments (argc, in essence)
-             * b) a number of newline-separated argument strings equal to count
-             *
-             * After the zygote process reads these it will write the pid of
-             * the child or -1 on failure, followed by boolean to
-             * indicate whether a wrapper process was used.
-             */
-            final BufferedWriter writer = zygoteState.writer;
-            final DataInputStream inputStream = zygoteState.inputStream;
-
-            writer.write(Integer.toString(args.size()));
-            writer.newLine();
-
-            int sz = args.size();
-            for (int i = 0; i < sz; i++) {
-                String arg = args.get(i);
-                if (arg.indexOf('\n') >= 0) {
-                    throw new ZygoteStartFailedEx(
-                            "embedded newlines not allowed");
-                }
-                writer.write(arg);
-                writer.newLine();
-            }
-
-            writer.flush();
-
-            // Should there be a timeout on this?
-            ProcessStartResult result = new ProcessStartResult();
-            result.pid = inputStream.readInt();
-            if (result.pid < 0) {
-                throw new ZygoteStartFailedEx("fork() failed");
-            }
-            result.usingWrapper = inputStream.readBoolean();
-            return result;
-        } catch (IOException ex) {
-            zygoteState.close();
-            throw new ZygoteStartFailedEx(ex);
-        }
-    }
-
-    /**
-     * Starts a new process via the zygote mechanism.
-     *
-     * @param processClass Class name whose static main() to run
-     * @param niceName 'nice' process name to appear in ps
-     * @param uid a POSIX uid that the new process should setuid() to
-     * @param gid a POSIX gid that the new process shuold setgid() to
-     * @param gids null-ok; a list of supplementary group IDs that the
-     * new process should setgroup() to.
-     * @param debugFlags Additional flags.
-     * @param targetSdkVersion The target SDK version for the app.
-     * @param seInfo null-ok SELinux information for the new process.
-     * @param abi the ABI the process should use.
-     * @param instructionSet null-ok the instruction set to use.
-     * @param appDataDir null-ok the data directory of the app.
-     * @param extraArgs Additional arguments to supply to the zygote process.
-     * @return An object that describes the result of the attempt to start the process.
-     * @throws ZygoteStartFailedEx if process start failed for any reason
-     */
-    private static ProcessStartResult startViaZygote(final String processClass,
-                                  final String niceName,
-                                  final int uid, final int gid,
-                                  final int[] gids,
-                                  int debugFlags, int mountExternal,
-                                  int targetSdkVersion,
-                                  String seInfo,
-                                  String abi,
-                                  String instructionSet,
-                                  String appDataDir,
-                                  String[] extraArgs)
-                                  throws ZygoteStartFailedEx {
-        synchronized(Process.class) {
-            ArrayList<String> argsForZygote = new ArrayList<String>();
-
-            // --runtime-args, --setuid=, --setgid=,
-            // and --setgroups= must go first
-            argsForZygote.add("--runtime-args");
-            argsForZygote.add("--setuid=" + uid);
-            argsForZygote.add("--setgid=" + gid);
-            if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
-                argsForZygote.add("--enable-jni-logging");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
-                argsForZygote.add("--enable-safemode");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
-                argsForZygote.add("--enable-debugger");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
-                argsForZygote.add("--enable-checkjni");
-            }
-            if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
-                argsForZygote.add("--generate-debug-info");
-            }
-            if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
-                argsForZygote.add("--always-jit");
-            }
-            if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
-                argsForZygote.add("--native-debuggable");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
-                argsForZygote.add("--enable-assert");
-            }
-            if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
-                argsForZygote.add("--mount-external-default");
-            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
-                argsForZygote.add("--mount-external-read");
-            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
-                argsForZygote.add("--mount-external-write");
-            }
-            argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
-
-            //TODO optionally enable debuger
-            //argsForZygote.add("--enable-debugger");
-
-            // --setgroups is a comma-separated list
-            if (gids != null && gids.length > 0) {
-                StringBuilder sb = new StringBuilder();
-                sb.append("--setgroups=");
-
-                int sz = gids.length;
-                for (int i = 0; i < sz; i++) {
-                    if (i != 0) {
-                        sb.append(',');
-                    }
-                    sb.append(gids[i]);
-                }
-
-                argsForZygote.add(sb.toString());
-            }
-
-            if (niceName != null) {
-                argsForZygote.add("--nice-name=" + niceName);
-            }
-
-            if (seInfo != null) {
-                argsForZygote.add("--seinfo=" + seInfo);
-            }
-
-            if (instructionSet != null) {
-                argsForZygote.add("--instruction-set=" + instructionSet);
-            }
-
-            if (appDataDir != null) {
-                argsForZygote.add("--app-data-dir=" + appDataDir);
-            }
-
-            argsForZygote.add(processClass);
-
-            if (extraArgs != null) {
-                for (String arg : extraArgs) {
-                    argsForZygote.add(arg);
-                }
-            }
-
-            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
-        }
-    }
-
-    /**
-     * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block and retry if the
-     * zygote is unresponsive. This method is a no-op if a connection is already open.
-     *
-     * @hide
-     */
-    public static void establishZygoteConnectionForAbi(String abi) {
-        try {
-            openZygoteSocketIfNeeded(abi);
-        } catch (ZygoteStartFailedEx ex) {
-            throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex);
-        }
-    }
-
-    /**
-     * Tries to open socket to Zygote process if not already open. If
-     * already open, does nothing.  May block and retry.
-     */
-    private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
-        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
-            try {
-                primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
-            } catch (IOException ioe) {
-                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
-            }
-        }
-
-        if (primaryZygoteState.matches(abi)) {
-            return primaryZygoteState;
-        }
-
-        // The primary zygote didn't match. Try the secondary.
-        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
-            try {
-            secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);
-            } catch (IOException ioe) {
-                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
-            }
-        }
-
-        if (secondaryZygoteState.matches(abi)) {
-            return secondaryZygoteState;
-        }
-
-        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
     }
 
     /**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
new file mode 100644
index 0000000..d7a7296
--- /dev/null
+++ b/core/java/android/os/ZygoteProcess.java
@@ -0,0 +1,447 @@
+/*
+ * 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.os;
+
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.util.Log;
+import com.android.internal.os.Zygote;
+import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/*package*/ class ZygoteStartFailedEx extends Exception {
+    ZygoteStartFailedEx(String s) {
+        super(s);
+    }
+
+    ZygoteStartFailedEx(Throwable cause) {
+        super(cause);
+    }
+
+    ZygoteStartFailedEx(String s, Throwable cause) {
+        super(s, cause);
+    }
+}
+
+/**
+ * Maintains communication state with the zygote processes. This class is responsible
+ * for the sockets opened to the zygotes and for starting processes on behalf of the
+ * {@link android.os.Process} class.
+ *
+ * {@hide}
+ */
+public class ZygoteProcess {
+    private static final String LOG_TAG = "ZygoteProcess";
+
+    /**
+     * The name of the socket used to communicate with the primary zygote.
+     */
+    private final String mSocket;
+
+    /**
+     * The name of the secondary (alternate ABI) zygote socket.
+     */
+    private final String mSecondarySocket;
+
+    public ZygoteProcess(String primarySocket, String secondarySocket) {
+        mSocket = primarySocket;
+        mSecondarySocket = secondarySocket;
+    }
+
+    /**
+     * State for communicating with the zygote process.
+     */
+    public static class ZygoteState {
+        final LocalSocket socket;
+        final DataInputStream inputStream;
+        final BufferedWriter writer;
+        final List<String> abiList;
+
+        boolean mClosed;
+
+        private ZygoteState(LocalSocket socket, DataInputStream inputStream,
+                BufferedWriter writer, List<String> abiList) {
+            this.socket = socket;
+            this.inputStream = inputStream;
+            this.writer = writer;
+            this.abiList = abiList;
+        }
+
+        public static ZygoteState connect(String socketAddress) throws IOException {
+            DataInputStream zygoteInputStream = null;
+            BufferedWriter zygoteWriter = null;
+            final LocalSocket zygoteSocket = new LocalSocket();
+
+            try {
+                zygoteSocket.connect(new LocalSocketAddress(socketAddress,
+                        LocalSocketAddress.Namespace.RESERVED));
+
+                zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
+
+                zygoteWriter = new BufferedWriter(new OutputStreamWriter(
+                        zygoteSocket.getOutputStream()), 256);
+            } catch (IOException ex) {
+                try {
+                    zygoteSocket.close();
+                } catch (IOException ignore) {
+                }
+
+                throw ex;
+            }
+
+            String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
+            Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);
+
+            return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
+                    Arrays.asList(abiListString.split(",")));
+        }
+
+        boolean matches(String abi) {
+            return abiList.contains(abi);
+        }
+
+        public void close() {
+            try {
+                socket.close();
+            } catch (IOException ex) {
+                Log.e(LOG_TAG,"I/O exception on routine close", ex);
+            }
+
+            mClosed = true;
+        }
+
+        boolean isClosed() {
+            return mClosed;
+        }
+    }
+
+    /**
+     * The state of the connection to the primary zygote.
+     */
+    private ZygoteState primaryZygoteState;
+
+    /**
+     * The state of the connection to the secondary zygote.
+     */
+    private ZygoteState secondaryZygoteState;
+
+    /**
+     * Start a new process.
+     *
+     * <p>If processes are enabled, a new process is created and the
+     * static main() function of a <var>processClass</var> is executed there.
+     * The process will continue running after this function returns.
+     *
+     * <p>If processes are not enabled, a new thread in the caller's
+     * process is created and main() of <var>processClass</var> called there.
+     *
+     * <p>The niceName parameter, if not an empty string, is a custom name to
+     * give to the process instead of using processClass.  This allows you to
+     * make easily identifyable processes even if you are using the same base
+     * <var>processClass</var> to start them.
+     *
+     * @param processClass The class to use as the process's main entry
+     *                     point.
+     * @param niceName A more readable name to use for the process.
+     * @param uid The user-id under which the process will run.
+     * @param gid The group-id under which the process will run.
+     * @param gids Additional group-ids associated with the process.
+     * @param debugFlags Additional flags.
+     * @param targetSdkVersion The target SDK version for the app.
+     * @param seInfo null-ok SELinux information for the new process.
+     * @param abi non-null the ABI this app should be started with.
+     * @param instructionSet null-ok the instruction set to use.
+     * @param appDataDir null-ok the data directory of the app.
+     * @param zygoteArgs Additional arguments to supply to the zygote process.
+     *
+     * @return An object that describes the result of the attempt to start the process.
+     * @throws RuntimeException on fatal start failure
+     */
+    public final Process.ProcessStartResult start(final String processClass,
+                                                  final String niceName,
+                                                  int uid, int gid, int[] gids,
+                                                  int debugFlags, int mountExternal,
+                                                  int targetSdkVersion,
+                                                  String seInfo,
+                                                  String abi,
+                                                  String instructionSet,
+                                                  String appDataDir,
+                                                  String[] zygoteArgs) {
+        try {
+            return startViaZygote(processClass, niceName, uid, gid, gids,
+                    debugFlags, mountExternal, targetSdkVersion, seInfo,
+                    abi, instructionSet, appDataDir, zygoteArgs);
+        } catch (ZygoteStartFailedEx ex) {
+            Log.e(LOG_TAG,
+                    "Starting VM process through Zygote failed");
+            throw new RuntimeException(
+                    "Starting VM process through Zygote failed", ex);
+        }
+    }
+
+    /** retry interval for opening a zygote socket */
+    static final int ZYGOTE_RETRY_MILLIS = 500;
+
+    /**
+     * Queries the zygote for the list of ABIS it supports.
+     *
+     * @throws ZygoteStartFailedEx if the query failed.
+     */
+    private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
+            throws IOException {
+        // Each query starts with the argument count (1 in this case)
+        writer.write("1");
+        // ... followed by a new-line.
+        writer.newLine();
+        // ... followed by our only argument.
+        writer.write("--query-abi-list");
+        writer.newLine();
+        writer.flush();
+
+        // The response is a length prefixed stream of ASCII bytes.
+        int numBytes = inputStream.readInt();
+        byte[] bytes = new byte[numBytes];
+        inputStream.readFully(bytes);
+
+        return new String(bytes, StandardCharsets.US_ASCII);
+    }
+
+    /**
+     * Sends an argument list to the zygote process, which starts a new child
+     * and returns the child's pid. Please note: the present implementation
+     * replaces newlines in the argument list with spaces.
+     *
+     * @throws ZygoteStartFailedEx if process start failed for any reason
+     */
+    private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
+            ZygoteState zygoteState, ArrayList<String> args)
+            throws ZygoteStartFailedEx {
+        try {
+            /**
+             * See com.android.internal.os.SystemZygoteInit.readArgumentList()
+             * Presently the wire format to the zygote process is:
+             * a) a count of arguments (argc, in essence)
+             * b) a number of newline-separated argument strings equal to count
+             *
+             * After the zygote process reads these it will write the pid of
+             * the child or -1 on failure, followed by boolean to
+             * indicate whether a wrapper process was used.
+             */
+            final BufferedWriter writer = zygoteState.writer;
+            final DataInputStream inputStream = zygoteState.inputStream;
+
+            writer.write(Integer.toString(args.size()));
+            writer.newLine();
+
+            int sz = args.size();
+            for (int i = 0; i < sz; i++) {
+                String arg = args.get(i);
+                if (arg.indexOf('\n') >= 0) {
+                    throw new ZygoteStartFailedEx(
+                            "embedded newlines not allowed");
+                }
+                writer.write(arg);
+                writer.newLine();
+            }
+
+            writer.flush();
+
+            // Should there be a timeout on this?
+            Process.ProcessStartResult result = new Process.ProcessStartResult();
+            result.pid = inputStream.readInt();
+            if (result.pid < 0) {
+                throw new ZygoteStartFailedEx("fork() failed");
+            }
+            result.usingWrapper = inputStream.readBoolean();
+            return result;
+        } catch (IOException ex) {
+            zygoteState.close();
+            throw new ZygoteStartFailedEx(ex);
+        }
+    }
+
+    /**
+     * Starts a new process via the zygote mechanism.
+     *
+     * @param processClass Class name whose static main() to run
+     * @param niceName 'nice' process name to appear in ps
+     * @param uid a POSIX uid that the new process should setuid() to
+     * @param gid a POSIX gid that the new process shuold setgid() to
+     * @param gids null-ok; a list of supplementary group IDs that the
+     * new process should setgroup() to.
+     * @param debugFlags Additional flags.
+     * @param targetSdkVersion The target SDK version for the app.
+     * @param seInfo null-ok SELinux information for the new process.
+     * @param abi the ABI the process should use.
+     * @param instructionSet null-ok the instruction set to use.
+     * @param appDataDir null-ok the data directory of the app.
+     * @param extraArgs Additional arguments to supply to the zygote process.
+     * @return An object that describes the result of the attempt to start the process.
+     * @throws ZygoteStartFailedEx if process start failed for any reason
+     */
+    private Process.ProcessStartResult startViaZygote(final String processClass,
+                                                      final String niceName,
+                                                      final int uid, final int gid,
+                                                      final int[] gids,
+                                                      int debugFlags, int mountExternal,
+                                                      int targetSdkVersion,
+                                                      String seInfo,
+                                                      String abi,
+                                                      String instructionSet,
+                                                      String appDataDir,
+                                                      String[] extraArgs)
+                                                      throws ZygoteStartFailedEx {
+        synchronized(Process.class) {
+            ArrayList<String> argsForZygote = new ArrayList<String>();
+
+            // --runtime-args, --setuid=, --setgid=,
+            // and --setgroups= must go first
+            argsForZygote.add("--runtime-args");
+            argsForZygote.add("--setuid=" + uid);
+            argsForZygote.add("--setgid=" + gid);
+            if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
+                argsForZygote.add("--enable-jni-logging");
+            }
+            if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
+                argsForZygote.add("--enable-safemode");
+            }
+            if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
+                argsForZygote.add("--enable-debugger");
+            }
+            if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
+                argsForZygote.add("--enable-checkjni");
+            }
+            if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
+                argsForZygote.add("--generate-debug-info");
+            }
+            if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
+                argsForZygote.add("--always-jit");
+            }
+            if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
+                argsForZygote.add("--native-debuggable");
+            }
+            if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
+                argsForZygote.add("--enable-assert");
+            }
+            if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
+                argsForZygote.add("--mount-external-default");
+            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
+                argsForZygote.add("--mount-external-read");
+            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
+                argsForZygote.add("--mount-external-write");
+            }
+            argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
+
+            //TODO optionally enable debuger
+            //argsForZygote.add("--enable-debugger");
+
+            // --setgroups is a comma-separated list
+            if (gids != null && gids.length > 0) {
+                StringBuilder sb = new StringBuilder();
+                sb.append("--setgroups=");
+
+                int sz = gids.length;
+                for (int i = 0; i < sz; i++) {
+                    if (i != 0) {
+                        sb.append(',');
+                    }
+                    sb.append(gids[i]);
+                }
+
+                argsForZygote.add(sb.toString());
+            }
+
+            if (niceName != null) {
+                argsForZygote.add("--nice-name=" + niceName);
+            }
+
+            if (seInfo != null) {
+                argsForZygote.add("--seinfo=" + seInfo);
+            }
+
+            if (instructionSet != null) {
+                argsForZygote.add("--instruction-set=" + instructionSet);
+            }
+
+            if (appDataDir != null) {
+                argsForZygote.add("--app-data-dir=" + appDataDir);
+            }
+
+            argsForZygote.add(processClass);
+
+            if (extraArgs != null) {
+                for (String arg : extraArgs) {
+                    argsForZygote.add(arg);
+                }
+            }
+
+            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
+        }
+    }
+
+    /**
+     * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block
+     * and retry if the zygote is unresponsive. This method is a no-op if a connection is
+     * already open.
+     */
+    public void establishZygoteConnectionForAbi(String abi) {
+        try {
+            openZygoteSocketIfNeeded(abi);
+        } catch (ZygoteStartFailedEx ex) {
+            throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex);
+        }
+    }
+
+    /**
+     * Tries to open socket to Zygote process if not already open. If
+     * already open, does nothing.  May block and retry.
+     */
+    private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
+        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
+            try {
+                primaryZygoteState = ZygoteState.connect(mSocket);
+            } catch (IOException ioe) {
+                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
+            }
+        }
+
+        if (primaryZygoteState.matches(abi)) {
+            return primaryZygoteState;
+        }
+
+        // The primary zygote didn't match. Try the secondary.
+        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
+            try {
+                secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
+            } catch (IOException ioe) {
+                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
+            }
+        }
+
+        if (secondaryZygoteState.matches(abi)) {
+            return secondaryZygoteState;
+        }
+
+        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
+    }
+}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index d217474..ad14a20 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -307,7 +307,7 @@
     private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster";
     private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster";
 
-    @SuppressWarnings("deprecated")
+    @SuppressWarnings("deprecation")
     private void initCpuClusters() {
         // Figure out how many CPU clusters we're dealing with
         final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT);
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index de671b1..c851e4e 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -230,7 +230,7 @@
      * @param classLoader the classLoader to load {@className} with
      */
     private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
-            throws ZygoteInit.MethodAndArgsCaller {
+            throws Zygote.MethodAndArgsCaller {
         Class<?> cl;
 
         try {
@@ -264,7 +264,7 @@
          * clears up all the stack frames that were required in setting
          * up the process.
          */
-        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
+        throw new Zygote.MethodAndArgsCaller(m, argv);
     }
 
     public static final void main(String[] argv) {
@@ -301,7 +301,7 @@
      * @param argv arg strings
      */
     public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
-            throws ZygoteInit.MethodAndArgsCaller {
+            throws Zygote.MethodAndArgsCaller {
         if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
 
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
@@ -324,14 +324,14 @@
      * @param argv arg strings
      */
     public static void wrapperInit(int targetSdkVersion, String[] argv)
-            throws ZygoteInit.MethodAndArgsCaller {
+            throws Zygote.MethodAndArgsCaller {
         if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
 
         applicationInit(targetSdkVersion, argv, null);
     }
 
     private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
-            throws ZygoteInit.MethodAndArgsCaller {
+            throws Zygote.MethodAndArgsCaller {
         // If the application calls System.exit(), terminate the process
         // immediately without running any shutdown hooks.  It is not possible to
         // shutdown an Android application gracefully.  Among other things, the
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
new file mode 100644
index 0000000..2ed7aa2
--- /dev/null
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -0,0 +1,32 @@
+/*
+ * 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.internal.os;
+
+/**
+ * Startup class for the WebView zygote process.
+ *
+ * See {@link ZygoteInit} for generic zygote startup documentation.
+ *
+ * @hide
+ */
+class WebViewZygoteInit {
+    public static final String TAG = "WebViewZygoteInit";
+
+    public static void main(String argv[]) {
+        throw new RuntimeException("Not implemented yet");
+    }
+}
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index c558cf8..594b6ab 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -74,14 +74,14 @@
                 }
             }
 
-            // Mimic Zygote preloading.
+            // Mimic system Zygote preloading.
             ZygoteInit.preload();
 
             // Launch the application.
             String[] runtimeArgs = new String[args.length - 2];
             System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length);
             RuntimeInit.wrapperInit(targetSdkVersion, runtimeArgs);
-        } catch (ZygoteInit.MethodAndArgsCaller caller) {
+        } catch (Zygote.MethodAndArgsCaller caller) {
             caller.run();
         }
     }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 66cc975..fc0ccb7 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -22,6 +22,9 @@
 import android.system.ErrnoException;
 import android.system.Os;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
 /** @hide */
 public final class Zygote {
     /*
@@ -191,4 +194,39 @@
             command.append(" '").append(arg.replace("'", "'\\''")).append("'");
         }
     }
+
+    /**
+     * Helper exception class which holds a method and arguments and
+     * can call them. This is used as part of a trampoline to get rid of
+     * the initial process setup stack frames.
+     */
+    public static class MethodAndArgsCaller extends Exception
+            implements Runnable {
+        /** method to call */
+        private final Method mMethod;
+
+        /** argument array */
+        private final String[] mArgs;
+
+        public MethodAndArgsCaller(Method method, String[] args) {
+            mMethod = method;
+            mArgs = args;
+        }
+
+        public void run() {
+            try {
+                mMethod.invoke(null, new Object[] { mArgs });
+            } catch (IllegalAccessException ex) {
+                throw new RuntimeException(ex);
+            } catch (InvocationTargetException ex) {
+                Throwable cause = ex.getCause();
+                if (cause instanceof RuntimeException) {
+                    throw (RuntimeException) cause;
+                } else if (cause instanceof Error) {
+                    throw (Error) cause;
+                }
+                throw new RuntimeException(ex);
+            }
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 85d84bb..132b022 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -117,7 +117,7 @@
 
     /**
      * Reads one start command from the command socket. If successful,
-     * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller}
+     * a child is forked and a {@link Zygote.MethodAndArgsCaller}
      * exception is thrown in that child while in the parent process,
      * the method returns normally. On failure, the child is not
      * spawned and messages are printed to the log and stderr. Returns
@@ -126,10 +126,10 @@
      *
      * @return false if command socket should continue to be read from, or
      * true if an end-of-file has been encountered.
-     * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
+     * @throws Zygote.MethodAndArgsCaller trampoline to invoke main()
      * method in child process
      */
-    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
+    boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
 
         String args[];
         Arguments parsedArgs = null;
@@ -214,7 +214,7 @@
                 fdsToClose[0] = fd.getInt$();
             }
 
-            fd = ZygoteInit.getServerSocketFileDescriptor();
+            fd = zygoteServer.getServerSocketFileDescriptor();
 
             if (fd != null) {
                 fdsToClose[1] = fd.getInt$();
@@ -238,12 +238,13 @@
         try {
             if (pid == 0) {
                 // in child
+                zygoteServer.closeServerSocket();
                 IoUtils.closeQuietly(serverPipeFd);
                 serverPipeFd = null;
                 handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
 
                 // should never get here, the child is expected to either
-                // throw ZygoteInit.MethodAndArgsCaller or exec().
+                // throw Zygote.MethodAndArgsCaller or exec().
                 return true;
             } else {
                 // in parent...pid of < 0 means failure
@@ -712,12 +713,12 @@
      * @param newStderr null-ok; stream to use for stderr until stdio
      * is reopened.
      *
-     * @throws ZygoteInit.MethodAndArgsCaller on success to
+     * @throws Zygote.MethodAndArgsCaller on success to
      * trampoline to code that invokes static main.
      */
     private void handleChildProc(Arguments parsedArgs,
             FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
-            throws ZygoteInit.MethodAndArgsCaller {
+            throws Zygote.MethodAndArgsCaller {
         /**
          * By the time we get here, the native code has closed the two actual Zygote
          * socket connections, and substituted /dev/null in their place.  The LocalSocket
@@ -725,8 +726,6 @@
          */
 
         closeSocket();
-        ZygoteInit.closeServerSocket();
-
         if (descriptors != null) {
             try {
                 Os.dup2(descriptors[0], STDIN_FILENO);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index b57aea6..16dc133 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.os;
 
-import static android.system.OsConstants.POLLIN;
 import static android.system.OsConstants.S_IRWXG;
 import static android.system.OsConstants.S_IRWXO;
 
@@ -31,11 +30,11 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.os.ZygoteProcess;
 import android.security.keystore.AndroidKeyStoreProvider;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
-import android.system.StructPollfd;
 import android.text.Hyphenator;
 import android.util.EventLog;
 import android.util.Log;
@@ -52,17 +51,13 @@
 import libcore.io.IoUtils;
 
 import java.io.BufferedReader;
-import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.security.Security;
 import java.security.Provider;
-import java.util.ArrayList;
 
 /**
  * Startup class for the zygote process.
@@ -82,8 +77,6 @@
     private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
     private static final String PROPERTY_RUNNING_IN_CONTAINER = "ro.boot.container";
 
-    private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
-
     private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
     private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
 
@@ -94,11 +87,8 @@
 
     private static final String SOCKET_NAME_ARG = "--socket-name=";
 
-    private static LocalServerSocket sServerSocket;
-
     /**
-     * Used to pre-load resources.  We hold a global reference on it so it
-     * never gets destroyed.
+     * Used to pre-load resources.
      */
     private static Resources mResources;
 
@@ -110,78 +100,6 @@
     /** Controls whether we should preload resources during zygote init. */
     public static final boolean PRELOAD_RESOURCES = true;
 
-    /**
-     * Registers a server socket for zygote command connections
-     *
-     * @throws RuntimeException when open fails
-     */
-    private static void registerZygoteSocket(String socketName) {
-        if (sServerSocket == null) {
-            int fileDesc;
-            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
-            try {
-                String env = System.getenv(fullSocketName);
-                fileDesc = Integer.parseInt(env);
-            } catch (RuntimeException ex) {
-                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
-            }
-
-            try {
-                FileDescriptor fd = new FileDescriptor();
-                fd.setInt$(fileDesc);
-                sServerSocket = new LocalServerSocket(fd);
-            } catch (IOException ex) {
-                throw new RuntimeException(
-                        "Error binding to local socket '" + fileDesc + "'", ex);
-            }
-        }
-    }
-
-    /**
-     * Waits for and accepts a single command connection. Throws
-     * RuntimeException on failure.
-     */
-    private static ZygoteConnection acceptCommandPeer(String abiList) {
-        try {
-            return new ZygoteConnection(sServerSocket.accept(), abiList);
-        } catch (IOException ex) {
-            throw new RuntimeException(
-                    "IOException during accept()", ex);
-        }
-    }
-
-    /**
-     * Close and clean up zygote sockets. Called on shutdown and on the
-     * child's exit path.
-     */
-    static void closeServerSocket() {
-        try {
-            if (sServerSocket != null) {
-                FileDescriptor fd = sServerSocket.getFileDescriptor();
-                sServerSocket.close();
-                if (fd != null) {
-                    Os.close(fd);
-                }
-            }
-        } catch (IOException ex) {
-            Log.e(TAG, "Zygote:  error closing sockets", ex);
-        } catch (ErrnoException ex) {
-            Log.e(TAG, "Zygote:  error closing descriptor", ex);
-        }
-
-        sServerSocket = null;
-    }
-
-    /**
-     * Return the server socket's underlying file descriptor, so that
-     * ZygoteConnection can pass it to the native code for proper
-     * closure after a child process is forked off.
-     */
-
-    static FileDescriptor getServerSocketFileDescriptor() {
-        return sServerSocket.getFileDescriptor();
-    }
-
     private static final int UNPRIVILEGED_UID = 9999;
     private static final int UNPRIVILEGED_GID = 9999;
 
@@ -504,9 +422,7 @@
      */
     private static void handleSystemServerProcess(
             ZygoteConnection.Arguments parsedArgs)
-            throws ZygoteInit.MethodAndArgsCaller {
-
-        closeServerSocket();
+            throws Zygote.MethodAndArgsCaller {
 
         // set umask to 0077 so new files and directories will default to owner-only permissions.
         Os.umask(S_IRWXG | S_IRWXO);
@@ -609,8 +525,8 @@
     /**
      * Prepare the arguments and fork for the system server process.
      */
-    private static boolean startSystemServer(String abiList, String socketName)
-            throws MethodAndArgsCaller, RuntimeException {
+    private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer)
+            throws Zygote.MethodAndArgsCaller, RuntimeException {
         long capabilities = posixCapabilitiesAsBits(
             OsConstants.CAP_IPC_LOCK,
             OsConstants.CAP_KILL,
@@ -666,6 +582,7 @@
                 waitForSecondaryZygote(socketName);
             }
 
+            zygoteServer.closeServerSocket();
             handleSystemServerProcess(parsedArgs);
         }
 
@@ -687,6 +604,8 @@
     }
 
     public static void main(String argv[]) {
+        ZygoteServer zygoteServer = new ZygoteServer();
+
         // Mark zygote start. This ensures that thread creation will throw
         // an error.
         ZygoteHooks.startZygoteNoThreadCreation();
@@ -716,7 +635,7 @@
                 throw new RuntimeException("No ABI list supplied.");
             }
 
-            registerZygoteSocket(socketName);
+            zygoteServer.registerServerSocket(socketName);
             Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygotePreload");
             EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                 SystemClock.uptimeMillis());
@@ -733,8 +652,6 @@
             gcAndFinalize();
             Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
 
-            Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
-
             // Disable tracing so that forked processes do not inherit stale tracing tags from
             // Zygote.
             Trace.setTracingEnabled(false);
@@ -745,18 +662,18 @@
             ZygoteHooks.stopZygoteNoThreadCreation();
 
             if (startSystemServer) {
-                startSystemServer(abiList, socketName);
+                startSystemServer(abiList, socketName, zygoteServer);
             }
 
             Log.i(TAG, "Accepting command socket connections");
-            runSelectLoop(abiList);
+            zygoteServer.runSelectLoop(abiList);
 
-            closeServerSocket();
-        } catch (MethodAndArgsCaller caller) {
+            zygoteServer.closeServerSocket();
+        } catch (Zygote.MethodAndArgsCaller caller) {
             caller.run();
         } catch (RuntimeException ex) {
-            Log.e(TAG, "Zygote died with exception", ex);
-            closeServerSocket();
+            Log.e(TAG, "System zygote died with exception", ex);
+            zygoteServer.closeServerSocket();
             throw ex;
         }
     }
@@ -777,7 +694,8 @@
                 Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
         while (true) {
             try {
-                final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName);
+                final ZygoteProcess.ZygoteState zs =
+                        ZygoteProcess.ZygoteState.connect(otherZygoteName);
                 zs.close();
                 break;
             } catch (IOException ioe) {
@@ -792,89 +710,8 @@
     }
 
     /**
-     * Runs the zygote process's select loop. Accepts new connections as
-     * they happen, and reads commands from connections one spawn-request's
-     * worth at a time.
-     *
-     * @throws MethodAndArgsCaller in a child process when a main() should
-     * be executed.
-     */
-    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
-        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
-        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
-
-        fds.add(sServerSocket.getFileDescriptor());
-        peers.add(null);
-
-        while (true) {
-            StructPollfd[] pollFds = new StructPollfd[fds.size()];
-            for (int i = 0; i < pollFds.length; ++i) {
-                pollFds[i] = new StructPollfd();
-                pollFds[i].fd = fds.get(i);
-                pollFds[i].events = (short) POLLIN;
-            }
-            try {
-                Os.poll(pollFds, -1);
-            } catch (ErrnoException ex) {
-                throw new RuntimeException("poll failed", ex);
-            }
-            for (int i = pollFds.length - 1; i >= 0; --i) {
-                if ((pollFds[i].revents & POLLIN) == 0) {
-                    continue;
-                }
-                if (i == 0) {
-                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
-                    peers.add(newPeer);
-                    fds.add(newPeer.getFileDesciptor());
-                } else {
-                    boolean done = peers.get(i).runOnce();
-                    if (done) {
-                        peers.remove(i);
-                        fds.remove(i);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
      * Class not instantiable.
      */
     private ZygoteInit() {
     }
-
-    /**
-     * Helper exception class which holds a method and arguments and
-     * can call them. This is used as part of a trampoline to get rid of
-     * the initial process setup stack frames.
-     */
-    public static class MethodAndArgsCaller extends Exception
-            implements Runnable {
-        /** method to call */
-        private final Method mMethod;
-
-        /** argument array */
-        private final String[] mArgs;
-
-        public MethodAndArgsCaller(Method method, String[] args) {
-            mMethod = method;
-            mArgs = args;
-        }
-
-        public void run() {
-            try {
-                mMethod.invoke(null, new Object[] { mArgs });
-            } catch (IllegalAccessException ex) {
-                throw new RuntimeException(ex);
-            } catch (InvocationTargetException ex) {
-                Throwable cause = ex.getCause();
-                if (cause instanceof RuntimeException) {
-                    throw (RuntimeException) cause;
-                } else if (cause instanceof Error) {
-                    throw (Error) cause;
-                }
-                throw new RuntimeException(ex);
-            }
-        }
-    }
 }
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
new file mode 100644
index 0000000..ab876410
--- /dev/null
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2007 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.internal.os;
+
+import static android.system.OsConstants.POLLIN;
+
+import android.net.LocalServerSocket;
+import android.system.Os;
+import android.system.ErrnoException;
+import android.system.StructPollfd;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+
+/**
+ * Server socket class for zygote processes.
+ *
+ * Provides functions to wait for commands on a UNIX domain socket, and fork
+ * off child processes that inherit the initial state of the VM.%
+ *
+ * Please see {@link ZygoteConnection.Arguments} for documentation on the
+ * client protocol.
+ */
+class ZygoteServer {
+    public static final String TAG = "ZygoteServer";
+
+    private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
+
+    private LocalServerSocket mServerSocket;
+
+    ZygoteServer() {
+    }
+
+    /**
+     * Registers a server socket for zygote command connections
+     *
+     * @throws RuntimeException when open fails
+     */
+    void registerServerSocket(String socketName) {
+        if (mServerSocket == null) {
+            int fileDesc;
+            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
+            try {
+                String env = System.getenv(fullSocketName);
+                fileDesc = Integer.parseInt(env);
+            } catch (RuntimeException ex) {
+                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
+            }
+
+            try {
+                FileDescriptor fd = new FileDescriptor();
+                fd.setInt$(fileDesc);
+                mServerSocket = new LocalServerSocket(fd);
+            } catch (IOException ex) {
+                throw new RuntimeException(
+                        "Error binding to local socket '" + fileDesc + "'", ex);
+            }
+        }
+    }
+
+    /**
+     * Waits for and accepts a single command connection. Throws
+     * RuntimeException on failure.
+     */
+    private ZygoteConnection acceptCommandPeer(String abiList) {
+        try {
+            return new ZygoteConnection(mServerSocket.accept(), abiList);
+        } catch (IOException ex) {
+            throw new RuntimeException(
+                    "IOException during accept()", ex);
+        }
+    }
+
+    /**
+     * Close and clean up zygote sockets. Called on shutdown and on the
+     * child's exit path.
+     */
+    void closeServerSocket() {
+        try {
+            if (mServerSocket != null) {
+                FileDescriptor fd = mServerSocket.getFileDescriptor();
+                mServerSocket.close();
+                if (fd != null) {
+                    Os.close(fd);
+                }
+            }
+        } catch (IOException ex) {
+            Log.e(TAG, "Zygote:  error closing sockets", ex);
+        } catch (ErrnoException ex) {
+            Log.e(TAG, "Zygote:  error closing descriptor", ex);
+        }
+
+        mServerSocket = null;
+    }
+
+    /**
+     * Return the server socket's underlying file descriptor, so that
+     * ZygoteConnection can pass it to the native code for proper
+     * closure after a child process is forked off.
+     */
+
+    FileDescriptor getServerSocketFileDescriptor() {
+        return mServerSocket.getFileDescriptor();
+    }
+
+    /**
+     * Runs the zygote process's select loop. Accepts new connections as
+     * they happen, and reads commands from connections one spawn-request's
+     * worth at a time.
+     *
+     * @throws Zygote.MethodAndArgsCaller in a child process when a main()
+     * should be executed.
+     */
+    void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
+        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
+        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
+
+        fds.add(mServerSocket.getFileDescriptor());
+        peers.add(null);
+
+        while (true) {
+            StructPollfd[] pollFds = new StructPollfd[fds.size()];
+            for (int i = 0; i < pollFds.length; ++i) {
+                pollFds[i] = new StructPollfd();
+                pollFds[i].fd = fds.get(i);
+                pollFds[i].events = (short) POLLIN;
+            }
+            try {
+                Os.poll(pollFds, -1);
+            } catch (ErrnoException ex) {
+                throw new RuntimeException("poll failed", ex);
+            }
+            for (int i = pollFds.length - 1; i >= 0; --i) {
+                if ((pollFds[i].revents & POLLIN) == 0) {
+                    continue;
+                }
+                if (i == 0) {
+                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
+                    peers.add(newPeer);
+                    fds.add(newPeer.getFileDesciptor());
+                } else {
+                    boolean done = peers.get(i).runOnce(this);
+                    if (done) {
+                        peers.remove(i);
+                        fds.remove(i);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/util/WakeupMessage.java b/core/java/com/android/internal/util/WakeupMessage.java
index 2653745..7d222c7 100644
--- a/core/java/com/android/internal/util/WakeupMessage.java
+++ b/core/java/com/android/internal/util/WakeupMessage.java
@@ -45,24 +45,32 @@
     protected final String mCmdName;
     @VisibleForTesting
     protected final int mCmd, mArg1, mArg2;
+    @VisibleForTesting
+    protected final Object mObj;
     private boolean mScheduled;
 
     public WakeupMessage(Context context, Handler handler,
-            String cmdName, int cmd, int arg1, int arg2) {
+            String cmdName, int cmd, int arg1, int arg2, Object obj) {
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         mHandler = handler;
         mCmdName = cmdName;
         mCmd = cmd;
         mArg1 = arg1;
         mArg2 = arg2;
+        mObj = obj;
     }
 
     public WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1) {
-        this(context, handler, cmdName, cmd, arg1, 0);
+        this(context, handler, cmdName, cmd, arg1, 0, null);
+    }
+
+    public WakeupMessage(Context context, Handler handler,
+            String cmdName, int cmd, int arg1, int arg2) {
+        this(context, handler, cmdName, cmd, arg1, arg2, null);
     }
 
     public WakeupMessage(Context context, Handler handler, String cmdName, int cmd) {
-        this(context, handler, cmdName, cmd, 0, 0);
+        this(context, handler, cmdName, cmd, 0, 0, null);
     }
 
     /**
@@ -99,7 +107,7 @@
             mScheduled = false;
         }
         if (stillScheduled) {
-            Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2);
+            Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
             mHandler.handleMessage(msg);
             msg.recycle();
         }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 60e888d..7e1a0ab 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -80,6 +80,10 @@
     android_text_AndroidBidi.cpp \
     android_text_StaticLayout.cpp \
     android_os_Debug.cpp \
+    android_os_HwBinder.cpp \
+    android_os_HwBlob.cpp \
+    android_os_HwParcel.cpp \
+    android_os_HwRemoteBinder.cpp \
     android_os_MemoryFile.cpp \
     android_os_MessageQueue.cpp \
     android_os_Parcel.cpp \
@@ -177,7 +181,8 @@
     com_android_internal_os_PathClassLoaderFactory.cpp \
     com_android_internal_os_Zygote.cpp \
     com_android_internal_util_VirtualRefBasePtr.cpp \
-    com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
+    com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp \
+    hwbinder/EphemeralStorage.cpp \
 
 LOCAL_C_INCLUDES += \
     $(JNI_H_INCLUDE) \
@@ -260,6 +265,8 @@
     libradio_metadata \
     libnativeloader \
     libmemunreachable \
+    libhidl \
+    libhwbinder \
 
 LOCAL_SHARED_LIBRARIES += \
     libhwui \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6cbc06c..07392c4 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -156,6 +156,10 @@
 extern int register_android_database_SQLiteDebug(JNIEnv* env);
 extern int register_android_nio_utils(JNIEnv* env);
 extern int register_android_os_Debug(JNIEnv* env);
+extern int register_android_os_HwBinder(JNIEnv *env);
+extern int register_android_os_HwBlob(JNIEnv *env);
+extern int register_android_os_HwParcel(JNIEnv *env);
+extern int register_android_os_HwRemoteBinder(JNIEnv *env);
 extern int register_android_os_MessageQueue(JNIEnv* env);
 extern int register_android_os_Parcel(JNIEnv* env);
 extern int register_android_os_SELinux(JNIEnv* env);
@@ -1287,6 +1291,10 @@
     REG_JNI(register_android_os_SystemProperties),
     REG_JNI(register_android_os_Binder),
     REG_JNI(register_android_os_Parcel),
+    REG_JNI(register_android_os_HwBinder),
+    REG_JNI(register_android_os_HwBlob),
+    REG_JNI(register_android_os_HwParcel),
+    REG_JNI(register_android_os_HwRemoteBinder),
     REG_JNI(register_android_nio_utils),
     REG_JNI(register_android_graphics_Canvas),
     REG_JNI(register_android_graphics_Graphics),
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
new file mode 100644
index 0000000..7da0314
--- /dev/null
+++ b/core/jni/android_os_HwBinder.cpp
@@ -0,0 +1,293 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android_os_HwBinder"
+#include <android-base/logging.h>
+
+#include "android_os_HwBinder.h"
+
+#include "android_os_HwParcel.h"
+#include "android_os_HwRemoteBinder.h"
+
+#include <JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <hidl/IServiceManager.h>
+#include <hidl/Status.h>
+#include <hwbinder/ProcessState.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+#include "core_jni_helpers.h"
+
+using android::AndroidRuntime;
+
+#define PACKAGE_PATH    "android/os"
+#define CLASS_NAME      "HwBinder"
+#define CLASS_PATH      PACKAGE_PATH "/" CLASS_NAME
+
+namespace android {
+
+static struct fields_t {
+    jfieldID contextID;
+    jmethodID onTransactID;
+
+} gFields;
+
+// static
+void JHwBinder::InitClass(JNIEnv *env) {
+    ScopedLocalRef<jclass> clazz(
+            env, FindClassOrDie(env, CLASS_PATH));
+
+    gFields.contextID =
+        GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J");
+
+    gFields.onTransactID =
+        GetMethodIDOrDie(
+                env,
+                clazz.get(),
+                "onTransact",
+                "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V");
+}
+
+// static
+sp<JHwBinder> JHwBinder::SetNativeContext(
+        JNIEnv *env, jobject thiz, const sp<JHwBinder> &context) {
+    sp<JHwBinder> old =
+        (JHwBinder *)env->GetLongField(thiz, gFields.contextID);
+
+    if (context != NULL) {
+        context->incStrong(NULL /* id */);
+    }
+
+    if (old != NULL) {
+        old->decStrong(NULL /* id */);
+    }
+
+    env->SetLongField(thiz, gFields.contextID, (long)context.get());
+
+    return old;
+}
+
+// static
+sp<JHwBinder> JHwBinder::GetNativeContext(
+        JNIEnv *env, jobject thiz) {
+    return (JHwBinder *)env->GetLongField(thiz, gFields.contextID);
+}
+
+JHwBinder::JHwBinder(JNIEnv *env, jobject thiz) {
+    jclass clazz = env->GetObjectClass(thiz);
+    CHECK(clazz != NULL);
+
+    mClass = (jclass)env->NewGlobalRef(clazz);
+    mObject = env->NewWeakGlobalRef(thiz);
+}
+
+JHwBinder::~JHwBinder() {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    env->DeleteWeakGlobalRef(mObject);
+    mObject = NULL;
+
+    env->DeleteGlobalRef(mClass);
+    mClass = NULL;
+}
+
+status_t JHwBinder::onTransact(
+        uint32_t code,
+        const hardware::Parcel &data,
+        hardware::Parcel *reply,
+        uint32_t flags,
+        TransactCallback callback) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    ScopedLocalRef<jobject> requestObj(env, JHwParcel::NewObject(env));
+    JHwParcel::GetNativeContext(env, requestObj.get())->setParcel(
+            const_cast<hardware::Parcel *>(&data), false /* assumeOwnership */);
+
+    ScopedLocalRef<jobject> replyObj(env, JHwParcel::NewObject(env));
+
+    sp<JHwParcel> replyContext =
+        JHwParcel::GetNativeContext(env, replyObj.get());
+
+    replyContext->setParcel(reply, false /* assumeOwnership */);
+    replyContext->setTransactCallback(callback);
+
+    env->CallVoidMethod(
+            mObject,
+            gFields.onTransactID,
+            code,
+            requestObj.get(),
+            replyObj.get(),
+            flags);
+
+    status_t err = OK;
+
+    if (!replyContext->wasSent()) {
+        // The implementation never finished the transaction.
+        err = UNKNOWN_ERROR;  // XXX special error code instead?
+
+        reply->setDataPosition(0 /* pos */);
+    }
+
+    // Release all temporary storage now that scatter-gather data
+    // has been consolidated, either by calling the TransactCallback,
+    // if wasSent() == true or clearing the reply parcel (setDataOffset above).
+    replyContext->getStorage()->release(env);
+
+    // We cannot permanently pass ownership of "data" and "reply" over to their
+    // Java object wrappers (we don't own them ourselves).
+
+    JHwParcel::GetNativeContext(env, requestObj.get())->setParcel(
+            NULL /* parcel */, false /* assumeOwnership */);
+
+    replyContext->setParcel(
+            NULL /* parcel */, false /* assumeOwnership */);
+
+    return err;
+}
+
+}  // namespace android
+
+////////////////////////////////////////////////////////////////////////////////
+
+using namespace android;
+
+static void releaseNativeContext(void *nativeContext) {
+    sp<JHwBinder> binder = (JHwBinder *)nativeContext;
+
+    if (binder != NULL) {
+        binder->decStrong(NULL /* id */);
+    }
+}
+
+static jlong JHwBinder_native_init(JNIEnv *env) {
+    JHwBinder::InitClass(env);
+
+    return reinterpret_cast<jlong>(&releaseNativeContext);
+}
+
+static void JHwBinder_native_setup(JNIEnv *env, jobject thiz) {
+    sp<JHwBinder> context = new JHwBinder(env, thiz);
+
+    JHwBinder::SetNativeContext(env, thiz, context);
+}
+
+static void JHwBinder_native_transact(
+        JNIEnv * /* env */,
+        jobject /* thiz */,
+        jint /* code */,
+        jobject /* requestObj */,
+        jobject /* replyObj */,
+        jint /* flags */) {
+    CHECK(!"Should not be here");
+}
+
+static void JHwBinder_native_registerService(
+        JNIEnv *env, jobject thiz, jstring serviceNameObj) {
+    if (serviceNameObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+
+    const jchar *serviceName = env->GetStringCritical(serviceNameObj, NULL);
+
+    if (serviceName == NULL) {
+        return;  // XXX exception already pending?
+    }
+
+    const hardware::hidl_version kVersion = hardware::make_hidl_version(1, 0);
+
+    sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz);
+
+    status_t err = hardware::defaultServiceManager()->addService(
+                String16(
+                    reinterpret_cast<const char16_t *>(serviceName),
+                    env->GetStringLength(serviceNameObj)),
+                binder,
+                kVersion);
+
+    env->ReleaseStringCritical(serviceNameObj, serviceName);
+    serviceName = NULL;
+
+    if (err == OK) {
+        LOG(INFO) << "Starting thread pool.";
+        ::android::hardware::ProcessState::self()->startThreadPool();
+    }
+
+    signalExceptionForError(env, err);
+}
+
+static jobject JHwBinder_native_getService(
+        JNIEnv *env, jclass /* clazzObj */, jstring serviceNameObj) {
+    if (serviceNameObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return NULL;
+    }
+
+    const jchar *serviceName = env->GetStringCritical(serviceNameObj, NULL);
+
+    if (serviceName == NULL) {
+        return NULL;  // XXX exception already pending?
+    }
+
+    const hardware::hidl_version kVersion = hardware::make_hidl_version(1, 0);
+
+    LOG(INFO) << "looking for service '"
+              << String8(String16(
+                          reinterpret_cast<const char16_t *>(serviceName),
+                          env->GetStringLength(serviceNameObj))).string()
+              << "'";
+
+    sp<hardware::IBinder> service =
+        hardware::defaultServiceManager()->getService(
+                String16(
+                    reinterpret_cast<const char16_t *>(serviceName),
+                    env->GetStringLength(serviceNameObj)),
+                kVersion);
+
+    env->ReleaseStringCritical(serviceNameObj, serviceName);
+    serviceName = NULL;
+
+    if (service == NULL) {
+        signalExceptionForError(env, NAME_NOT_FOUND);
+        return NULL;
+    }
+
+    return JHwRemoteBinder::NewObject(env, service);
+}
+
+static JNINativeMethod gMethods[] = {
+    { "native_init", "()J", (void *)JHwBinder_native_init },
+    { "native_setup", "()V", (void *)JHwBinder_native_setup },
+
+    { "transact",
+        "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V",
+        (void *)JHwBinder_native_transact },
+
+    { "registerService", "(Ljava/lang/String;)V",
+        (void *)JHwBinder_native_registerService },
+
+    { "getService", "(Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;",
+        (void *)JHwBinder_native_getService },
+};
+
+namespace android {
+
+int register_android_os_HwBinder(JNIEnv *env) {
+    return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
+}
+
+}  // namespace android
diff --git a/core/jni/android_os_HwBinder.h b/core/jni/android_os_HwBinder.h
new file mode 100644
index 0000000..2ebc381
--- /dev/null
+++ b/core/jni/android_os_HwBinder.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_OS_HW_BINDER_H
+#define _ANDROID_OS_HW_BINDER_H
+
+#include <android-base/macros.h>
+#include <hwbinder/Binder.h>
+#include <jni.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct JHwBinder : public hardware::BBinder {
+    static void InitClass(JNIEnv *env);
+
+    static sp<JHwBinder> SetNativeContext(
+            JNIEnv *env, jobject thiz, const sp<JHwBinder> &context);
+
+    static sp<JHwBinder> GetNativeContext(JNIEnv *env, jobject thiz);
+
+    JHwBinder(JNIEnv *env, jobject thiz);
+
+protected:
+    virtual ~JHwBinder();
+
+    virtual status_t onTransact(
+            uint32_t code,
+            const hardware::Parcel &data,
+            hardware::Parcel *reply,
+            uint32_t flags,
+            TransactCallback callback);
+
+private:
+    jclass mClass;
+    jobject mObject;
+
+    DISALLOW_COPY_AND_ASSIGN(JHwBinder);
+};
+
+int register_android_os_HwBinder(JNIEnv *env);
+
+}  // namespace android
+
+#endif  // _ANDROID_OS_HW_BINDER_H
+
+
diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp
new file mode 100644
index 0000000..b2dee06
--- /dev/null
+++ b/core/jni/android_os_HwBlob.cpp
@@ -0,0 +1,452 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android_os_HwBlob"
+#include <android-base/logging.h>
+
+#include "android_os_HwBlob.h"
+
+#include "android_os_HwParcel.h"
+
+#include <JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <hidl/Status.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+#include "core_jni_helpers.h"
+
+using android::AndroidRuntime;
+using android::hardware::hidl_string;
+
+#define PACKAGE_PATH    "android/os"
+#define CLASS_NAME      "HwBlob"
+#define CLASS_PATH      PACKAGE_PATH "/" CLASS_NAME
+
+namespace android {
+
+static struct fields_t {
+    jfieldID contextID;
+    jmethodID constructID;
+
+} gFields;
+
+// static
+void JHwBlob::InitClass(JNIEnv *env) {
+    ScopedLocalRef<jclass> clazz(
+            env, FindClassOrDie(env, CLASS_PATH));
+
+    gFields.contextID =
+        GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J");
+
+    gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "(I)V");
+}
+
+// static
+sp<JHwBlob> JHwBlob::SetNativeContext(
+        JNIEnv *env, jobject thiz, const sp<JHwBlob> &context) {
+    sp<JHwBlob> old = (JHwBlob *)env->GetLongField(thiz, gFields.contextID);
+
+    if (context != NULL) {
+        context->incStrong(NULL /* id */);
+    }
+
+    if (old != NULL) {
+        old->decStrong(NULL /* id */);
+    }
+
+    env->SetLongField(thiz, gFields.contextID, (long)context.get());
+
+    return old;
+}
+
+// static
+sp<JHwBlob> JHwBlob::GetNativeContext(JNIEnv *env, jobject thiz) {
+    return (JHwBlob *)env->GetLongField(thiz, gFields.contextID);
+}
+
+JHwBlob::JHwBlob(JNIEnv *env, jobject thiz, size_t size)
+    : mBuffer(nullptr),
+      mSize(size),
+      mOwnsBuffer(true),
+      mHandle(0) {
+    jclass clazz = env->GetObjectClass(thiz);
+    CHECK(clazz != NULL);
+
+    mClass = (jclass)env->NewGlobalRef(clazz);
+    mObject = env->NewWeakGlobalRef(thiz);
+
+    if (size > 0) {
+        mBuffer = malloc(size);
+    }
+}
+
+JHwBlob::~JHwBlob() {
+    if (mOwnsBuffer) {
+        free(mBuffer);
+        mBuffer = nullptr;
+    }
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    env->DeleteWeakGlobalRef(mObject);
+    mObject = NULL;
+
+    env->DeleteGlobalRef(mClass);
+    mClass = NULL;
+}
+
+void JHwBlob::setTo(const void *ptr, size_t handle) {
+    CHECK_EQ(mSize, 0u);
+    CHECK(mBuffer == nullptr);
+
+    mBuffer = const_cast<void *>(ptr);
+    mSize = SIZE_MAX;  // XXX
+    mOwnsBuffer = false;
+    mHandle = handle;
+}
+
+status_t JHwBlob::getHandle(size_t *handle) const {
+    if (mOwnsBuffer) {
+        return INVALID_OPERATION;
+    }
+
+    *handle = mHandle;
+
+    return OK;
+}
+
+status_t JHwBlob::read(size_t offset, void *data, size_t size) const {
+    if (offset + size > mSize) {
+        return -ERANGE;
+    }
+
+    memcpy(data, (const uint8_t *)mBuffer + offset, size);
+
+    return OK;
+}
+
+status_t JHwBlob::write(size_t offset, const void *data, size_t size) {
+    if (offset + size > mSize) {
+        return -ERANGE;
+    }
+
+    memcpy((uint8_t *)mBuffer + offset, data, size);
+
+    return OK;
+}
+
+status_t JHwBlob::getString(size_t offset, const hidl_string **s) const {
+    if ((offset + sizeof(hidl_string)) > mSize) {
+        return -ERANGE;
+    }
+
+    *s = reinterpret_cast<const hidl_string *>(
+            (const uint8_t *)mBuffer + offset);
+
+    return OK;
+}
+
+const void *JHwBlob::data() const {
+    return mBuffer;
+}
+
+size_t JHwBlob::size() const {
+    return mSize;
+}
+
+status_t JHwBlob::putBlob(size_t offset, const sp<JHwBlob> &blob) {
+    size_t index = mSubBlobs.add();
+    BlobInfo *info = &mSubBlobs.editItemAt(index);
+
+    info->mOffset = offset;
+    info->mBlob = blob;
+
+    const void *data = blob->data();
+
+    return write(offset, &data, sizeof(data));
+}
+
+status_t JHwBlob::writeToParcel(hardware::Parcel *parcel) const {
+    size_t handle;
+    status_t err = parcel->writeBuffer(data(), size(), &handle);
+
+    if (err != OK) {
+        return err;
+    }
+
+    for (size_t i = 0; i < mSubBlobs.size(); ++i) {
+        const BlobInfo &info = mSubBlobs[i];
+
+        err = info.mBlob->writeEmbeddedToParcel(parcel, handle, info.mOffset);
+
+        if (err != OK) {
+            return err;
+        }
+    }
+
+    return OK;
+}
+
+status_t JHwBlob::writeEmbeddedToParcel(
+        hardware::Parcel *parcel,
+        size_t parentHandle,
+        size_t parentOffset) const {
+    size_t handle;
+    status_t err = parcel->writeEmbeddedBuffer(
+            data(), size(), &handle, parentHandle, parentOffset);
+
+    if (err != OK) {
+        return err;
+    }
+
+    for (size_t i = 0; i < mSubBlobs.size(); ++i) {
+        const BlobInfo &info = mSubBlobs[i];
+
+        err = info.mBlob->writeEmbeddedToParcel(parcel, handle, info.mOffset);
+
+        if (err != OK) {
+            return err;
+        }
+    }
+
+    return OK;
+}
+
+// static
+jobject JHwBlob::NewObject(JNIEnv *env, const void *ptr, size_t handle) {
+    jobject obj = JHwBlob::NewObject(env, 0 /* size */);
+    JHwBlob::GetNativeContext(env, obj)->setTo(ptr, handle);
+
+    return obj;
+}
+
+// static
+jobject JHwBlob::NewObject(JNIEnv *env, size_t size) {
+    ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
+
+    jmethodID constructID =
+        GetMethodIDOrDie(env, clazz.get(), "<init>", "(I)V");
+
+    // XXX Again cannot refer to gFields.constructID because InitClass may
+    // not have been called yet.
+
+    return env->NewObject(clazz.get(), constructID, size);
+}
+
+}  // namespace android
+
+////////////////////////////////////////////////////////////////////////////////
+
+using namespace android;
+
+static void releaseNativeContext(void *nativeContext) {
+    sp<JHwBlob> parcel = (JHwBlob *)nativeContext;
+
+    if (parcel != NULL) {
+        parcel->decStrong(NULL /* id */);
+    }
+}
+
+static jlong JHwBlob_native_init(JNIEnv *env) {
+    JHwBlob::InitClass(env);
+
+    return reinterpret_cast<jlong>(&releaseNativeContext);
+}
+
+static void JHwBlob_native_setup(
+        JNIEnv *env, jobject thiz, jint size) {
+    sp<JHwBlob> context = new JHwBlob(env, thiz, size);
+
+    JHwBlob::SetNativeContext(env, thiz, context);
+}
+
+#define DEFINE_BLOB_GETTER(Suffix,Type)                                        \
+static Type JHwBlob_native_get ## Suffix(                                      \
+        JNIEnv *env, jobject thiz, jlong offset) {                             \
+    sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);                   \
+                                                                               \
+    Type x;                                                                    \
+    status_t err = blob->read(offset, &x, sizeof(x));                          \
+                                                                               \
+    if (err != OK) {                                                           \
+        signalExceptionForError(env, err);                                     \
+        return 0;                                                              \
+    }                                                                          \
+                                                                               \
+    return x;                                                                  \
+}
+
+DEFINE_BLOB_GETTER(Int8,jbyte)
+DEFINE_BLOB_GETTER(Int16,jshort)
+DEFINE_BLOB_GETTER(Int32,jint)
+DEFINE_BLOB_GETTER(Int64,jlong)
+DEFINE_BLOB_GETTER(Float,jfloat)
+DEFINE_BLOB_GETTER(Double,jdouble)
+
+static jboolean JHwBlob_native_getBool(
+        JNIEnv *env, jobject thiz, jlong offset) {
+    sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+
+    bool x;
+    status_t err = blob->read(offset, &x, sizeof(x));
+
+    if (err != OK) {
+        signalExceptionForError(env, err);
+        return 0;
+    }
+
+    return (jboolean)x;
+}
+
+static jstring JHwBlob_native_getString(
+        JNIEnv *env, jobject thiz, jlong offset) {
+    sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+
+    const hidl_string *s;
+    status_t err = blob->getString(offset, &s);
+
+    if (err != OK) {
+        signalExceptionForError(env, err);
+        return nullptr;
+    }
+
+    return env->NewStringUTF(s->c_str());
+}
+
+#define DEFINE_BLOB_PUTTER(Suffix,Type)                                        \
+static void JHwBlob_native_put ## Suffix(                                      \
+        JNIEnv *env, jobject thiz, jlong offset, Type x) {                     \
+                                                                               \
+    sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);                   \
+                                                                               \
+    status_t err = blob->write(offset, &x, sizeof(x));                         \
+                                                                               \
+    if (err != OK) {                                                           \
+        signalExceptionForError(env, err);                                     \
+    }                                                                          \
+}
+
+DEFINE_BLOB_PUTTER(Int8,jbyte)
+DEFINE_BLOB_PUTTER(Int16,jshort)
+DEFINE_BLOB_PUTTER(Int32,jint)
+DEFINE_BLOB_PUTTER(Int64,jlong)
+DEFINE_BLOB_PUTTER(Float,jfloat)
+DEFINE_BLOB_PUTTER(Double,jdouble)
+
+static void JHwBlob_native_putBool(
+        JNIEnv *env, jobject thiz, jlong offset, jboolean x) {
+
+    sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+
+    bool b = (bool)x;
+    status_t err = blob->write(offset, &b, sizeof(b));
+
+    if (err != OK) {
+        signalExceptionForError(env, err);
+    }
+}
+
+static void JHwBlob_native_putString(
+        JNIEnv *env, jobject thiz, jlong offset, jstring stringObj) {
+    if (stringObj == nullptr) {
+        jniThrowException(env, "java/lang/NullPointerException", nullptr);
+        return;
+    }
+
+    const char *s = env->GetStringUTFChars(stringObj, nullptr);
+
+    if (s == nullptr) {
+        return;
+    }
+
+    size_t size = strlen(s) + 1;
+    ScopedLocalRef<jobject> subBlobObj(env, JHwBlob::NewObject(env, size));
+    sp<JHwBlob> subBlob = JHwBlob::GetNativeContext(env, subBlobObj.get());
+    subBlob->write(0 /* offset */, s, size);
+
+    env->ReleaseStringUTFChars(stringObj, s);
+    s = nullptr;
+
+    hidl_string tmp;
+    tmp.setToExternal(static_cast<const char *>(subBlob->data()), size);
+
+    sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+    blob->write(offset, &tmp, sizeof(tmp));
+    blob->putBlob(offset + hidl_string::kOffsetOfBuffer, subBlob);
+}
+
+static void JHwBlob_native_putBlob(
+        JNIEnv *env, jobject thiz, jlong offset, jobject blobObj) {
+    if (blobObj == nullptr) {
+        jniThrowException(env, "java/lang/NullPointerException", nullptr);
+        return;
+    }
+
+    sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+    sp<JHwBlob> subBlob = JHwBlob::GetNativeContext(env, blobObj);
+
+    blob->putBlob(offset, subBlob);
+}
+
+static jlong JHwBlob_native_handle(JNIEnv *env, jobject thiz) {
+    size_t handle;
+    status_t err = JHwBlob::GetNativeContext(env, thiz)->getHandle(&handle);
+
+    if (err != OK) {
+        signalExceptionForError(env, err);
+        return 0;
+    }
+
+    return handle;
+}
+
+static JNINativeMethod gMethods[] = {
+    { "native_init", "()J", (void *)JHwBlob_native_init },
+    { "native_setup", "(I)V", (void *)JHwBlob_native_setup },
+
+    { "getBool", "(J)Z", (void *)JHwBlob_native_getBool },
+    { "getInt8", "(J)B", (void *)JHwBlob_native_getInt8 },
+    { "getInt16", "(J)S", (void *)JHwBlob_native_getInt16 },
+    { "getInt32", "(J)I", (void *)JHwBlob_native_getInt32 },
+    { "getInt64", "(J)J", (void *)JHwBlob_native_getInt64 },
+    { "getFloat", "(J)F", (void *)JHwBlob_native_getFloat },
+    { "getDouble", "(J)D", (void *)JHwBlob_native_getDouble },
+    { "getString", "(J)Ljava/lang/String;", (void *)JHwBlob_native_getString },
+
+    { "putBool", "(JZ)V", (void *)JHwBlob_native_putBool },
+    { "putInt8", "(JB)V", (void *)JHwBlob_native_putInt8 },
+    { "putInt16", "(JS)V", (void *)JHwBlob_native_putInt16 },
+    { "putInt32", "(JI)V", (void *)JHwBlob_native_putInt32 },
+    { "putInt64", "(JJ)V", (void *)JHwBlob_native_putInt64 },
+    { "putFloat", "(JF)V", (void *)JHwBlob_native_putFloat },
+    { "putDouble", "(JD)V", (void *)JHwBlob_native_putDouble },
+    { "putString", "(JLjava/lang/String;)V", (void *)JHwBlob_native_putString },
+
+    { "putBlob", "(JL" PACKAGE_PATH "/HwBlob;)V",
+        (void *)JHwBlob_native_putBlob },
+
+    { "handle", "()J", (void *)JHwBlob_native_handle },
+};
+
+namespace android {
+
+int register_android_os_HwBlob(JNIEnv *env) {
+    return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
+}
+
+}  // namespace android
+
diff --git a/core/jni/android_os_HwBlob.h b/core/jni/android_os_HwBlob.h
new file mode 100644
index 0000000..6bd82e9
--- /dev/null
+++ b/core/jni/android_os_HwBlob.h
@@ -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.
+ */
+
+#ifndef ANDROID_OS_HW_BLOB_H
+#define ANDROID_OS_HW_BLOB_H
+
+#include <android-base/macros.h>
+#include <jni.h>
+#include <hidl/HidlSupport.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct JHwBlob : public RefBase {
+    static void InitClass(JNIEnv *env);
+
+    static sp<JHwBlob> SetNativeContext(
+            JNIEnv *env, jobject thiz, const sp<JHwBlob> &context);
+
+    static sp<JHwBlob> GetNativeContext(JNIEnv *env, jobject thiz);
+
+    static jobject NewObject(JNIEnv *env, const void *ptr, size_t handle);
+    static jobject NewObject(JNIEnv *env, size_t size);
+
+    JHwBlob(JNIEnv *env, jobject thiz, size_t size);
+
+    void setTo(const void *ptr, size_t handle);
+
+    status_t getHandle(size_t *handle) const;
+
+    status_t read(size_t offset, void *data, size_t size) const;
+    status_t write(size_t offset, const void *data, size_t size);
+
+    status_t getString(
+            size_t offset, const android::hardware::hidl_string **s) const;
+
+    const void *data() const;
+    size_t size() const;
+
+    status_t putBlob(size_t offset, const sp<JHwBlob> &blob);
+
+    status_t writeToParcel(hardware::Parcel *parcel) const;
+
+    status_t writeEmbeddedToParcel(
+            hardware::Parcel *parcel,
+            size_t parentHandle,
+            size_t parentOffset) const;
+
+protected:
+    virtual ~JHwBlob();
+
+private:
+    struct BlobInfo {
+        size_t mOffset;
+        sp<JHwBlob> mBlob;
+    };
+
+    jclass mClass;
+    jobject mObject;
+
+    void *mBuffer;
+    size_t mSize;
+    bool mOwnsBuffer;
+
+    size_t mHandle;
+
+    Vector<BlobInfo> mSubBlobs;
+
+    DISALLOW_COPY_AND_ASSIGN(JHwBlob);
+};
+
+int register_android_os_HwBlob(JNIEnv *env);
+
+}  // namespace android
+
+#endif  // ANDROID_OS_HW_BLOB_H
+
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
new file mode 100644
index 0000000..d453b29
--- /dev/null
+++ b/core/jni/android_os_HwParcel.cpp
@@ -0,0 +1,1139 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android_os_HwParcel"
+#include <android-base/logging.h>
+
+#include "android_os_HwParcel.h"
+
+#include "android_os_HwBinder.h"
+#include "android_os_HwBlob.h"
+#include "android_os_HwRemoteBinder.h"
+
+#include <JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <hidl/Status.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+#include "core_jni_helpers.h"
+
+using android::AndroidRuntime;
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+#define PACKAGE_PATH    "android/os"
+#define CLASS_NAME      "HwParcel"
+#define CLASS_PATH      PACKAGE_PATH "/" CLASS_NAME
+
+namespace android {
+
+static struct fields_t {
+    jfieldID contextID;
+    jmethodID constructID;
+
+} gFields;
+
+void signalExceptionForError(JNIEnv *env, status_t err) {
+    switch (err) {
+        case OK:
+            break;
+
+        case NO_MEMORY:
+        {
+            jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+            break;
+        }
+
+        case INVALID_OPERATION:
+        {
+            jniThrowException(
+                    env, "java/lang/UnsupportedOperationException", NULL);
+            break;
+        }
+
+        case BAD_VALUE:
+        {
+            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+            break;
+        }
+
+        case -ERANGE:
+        case BAD_INDEX:
+        {
+            jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
+            break;
+        }
+
+        case BAD_TYPE:
+        {
+            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+            break;
+        }
+
+        case NAME_NOT_FOUND:
+        {
+            jniThrowException(env, "java/util/NoSuchElementException", NULL);
+            break;
+        }
+
+        case PERMISSION_DENIED:
+        {
+            jniThrowException(env, "java/lang/SecurityException", NULL);
+            break;
+        }
+
+        case NO_INIT:
+        {
+            jniThrowException(
+                    env, "java/lang/RuntimeException", "Not initialized");
+            break;
+        }
+
+        case ALREADY_EXISTS:
+        {
+            jniThrowException(
+                    env, "java/lang/RuntimeException", "Item already exists");
+            break;
+        }
+
+        default:
+        {
+            jniThrowException(
+                    env, "java/lang/RuntimeException", "Unknown error");
+
+            break;
+        }
+    }
+}
+
+// static
+void JHwParcel::InitClass(JNIEnv *env) {
+    ScopedLocalRef<jclass> clazz(
+            env, FindClassOrDie(env, CLASS_PATH));
+
+    gFields.contextID =
+        GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J");
+
+    gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "(Z)V");
+}
+
+// static
+sp<JHwParcel> JHwParcel::SetNativeContext(
+        JNIEnv *env, jobject thiz, const sp<JHwParcel> &context) {
+    sp<JHwParcel> old = (JHwParcel *)env->GetLongField(thiz, gFields.contextID);
+
+    if (context != NULL) {
+        context->incStrong(NULL /* id */);
+    }
+
+    if (old != NULL) {
+        old->decStrong(NULL /* id */);
+    }
+
+    env->SetLongField(thiz, gFields.contextID, (long)context.get());
+
+    return old;
+}
+
+// static
+sp<JHwParcel> JHwParcel::GetNativeContext(JNIEnv *env, jobject thiz) {
+    return (JHwParcel *)env->GetLongField(thiz, gFields.contextID);
+}
+
+JHwParcel::JHwParcel(JNIEnv *env, jobject thiz)
+    : mParcel(NULL),
+      mOwnsParcel(false),
+      mTransactCallback(nullptr),
+      mWasSent(false) {
+    jclass clazz = env->GetObjectClass(thiz);
+    CHECK(clazz != NULL);
+
+    mClass = (jclass)env->NewGlobalRef(clazz);
+    mObject = env->NewWeakGlobalRef(thiz);
+}
+
+JHwParcel::~JHwParcel() {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    mStorage.release(env);
+
+    setParcel(NULL, false /* assumeOwnership */);
+
+    env->DeleteWeakGlobalRef(mObject);
+    mObject = NULL;
+
+    env->DeleteGlobalRef(mClass);
+    mClass = NULL;
+}
+
+hardware::Parcel *JHwParcel::getParcel() {
+    return mParcel;
+}
+
+EphemeralStorage *JHwParcel::getStorage() {
+    return &mStorage;
+}
+
+void JHwParcel::setParcel(hardware::Parcel *parcel, bool assumeOwnership) {
+    if (mParcel && mOwnsParcel) {
+        delete mParcel;
+    }
+
+    mParcel = parcel;
+    mOwnsParcel = assumeOwnership;
+}
+
+// static
+jobject JHwParcel::NewObject(JNIEnv *env) {
+    ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
+
+    jmethodID constructID =
+        GetMethodIDOrDie(env, clazz.get(), "<init>", "(Z)V");
+
+    return env->NewObject(clazz.get(), constructID, false /* allocate */);
+}
+
+void JHwParcel::setTransactCallback(
+        ::android::hardware::IBinder::TransactCallback cb) {
+    mTransactCallback = cb;
+}
+
+void JHwParcel::send() {
+    CHECK(mTransactCallback != nullptr);
+    CHECK(mParcel != nullptr);
+
+    mTransactCallback(*mParcel);
+    mTransactCallback = nullptr;
+
+    mWasSent = true;
+}
+
+bool JHwParcel::wasSent() const {
+    return mWasSent;
+}
+
+}  // namespace android
+
+////////////////////////////////////////////////////////////////////////////////
+
+using namespace android;
+
+static void releaseNativeContext(void *nativeContext) {
+    sp<JHwParcel> parcel = (JHwParcel *)nativeContext;
+
+    if (parcel != NULL) {
+        parcel->decStrong(NULL /* id */);
+    }
+}
+
+static jlong JHwParcel_native_init(JNIEnv *env) {
+    JHwParcel::InitClass(env);
+
+    return reinterpret_cast<jlong>(&releaseNativeContext);
+}
+
+static void JHwParcel_native_setup(
+        JNIEnv *env, jobject thiz, jboolean allocate) {
+    sp<JHwParcel> context = new JHwParcel(env, thiz);
+
+    if (allocate) {
+        context->setParcel(new hardware::Parcel, true /* assumeOwnership */);
+    }
+
+    JHwParcel::SetNativeContext(env, thiz, context);
+}
+
+static void JHwParcel_native_writeInterfaceToken(
+        JNIEnv *env, jobject thiz, jstring interfaceNameObj) {
+    if (interfaceNameObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+
+    const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL);
+    if (interfaceName) {
+        hardware::Parcel *parcel =
+            JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+        status_t err = parcel->writeInterfaceToken(
+                String16(
+                    reinterpret_cast<const char16_t *>(interfaceName),
+                    env->GetStringLength(interfaceNameObj)));
+
+        env->ReleaseStringCritical(interfaceNameObj, interfaceName);
+        interfaceName = NULL;
+
+        signalExceptionForError(env, err);
+    }
+}
+
+static void JHwParcel_native_enforceInterface(
+        JNIEnv *env, jobject thiz, jstring interfaceNameObj) {
+    // XXX original binder Parcel enforceInterface implementation does some
+    // mysterious things regarding strictModePolicy(), figure out if we need
+    // that here as well.
+    if (interfaceNameObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+
+    const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL);
+    if (interfaceName) {
+        hardware::Parcel *parcel =
+            JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+        bool valid = parcel->enforceInterface(
+                String16(
+                    reinterpret_cast<const char16_t *>(interfaceName),
+                    env->GetStringLength(interfaceNameObj)));
+
+        env->ReleaseStringCritical(interfaceNameObj, interfaceName);
+        interfaceName = NULL;
+
+        if (!valid) {
+            jniThrowException(
+                    env,
+                    "java/lang/SecurityException",
+                    "HWBinder invocation to an incorrect interface");
+        }
+    }
+}
+
+#define DEFINE_PARCEL_WRITER(Suffix,Type)                               \
+static void JHwParcel_native_write ## Suffix(                           \
+        JNIEnv *env, jobject thiz, Type val) {                          \
+    hardware::Parcel *parcel =                                          \
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();            \
+                                                                        \
+    status_t err = parcel->write ## Suffix(val);                        \
+    signalExceptionForError(env, err);                                  \
+}
+
+#define DEFINE_PARCEL_READER(Suffix,Type)                               \
+static Type JHwParcel_native_read ## Suffix(                            \
+        JNIEnv *env, jobject thiz) {                                    \
+    hardware::Parcel *parcel =                                          \
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();            \
+                                                                        \
+    Type val;                                                           \
+    status_t err = parcel->read ## Suffix(&val);                        \
+    signalExceptionForError(env, err);                                  \
+                                                                        \
+    return val;                                                         \
+}
+
+DEFINE_PARCEL_WRITER(Bool,jboolean)
+DEFINE_PARCEL_WRITER(Int8,jbyte)
+DEFINE_PARCEL_WRITER(Int16,jshort)
+DEFINE_PARCEL_WRITER(Int32,jint)
+DEFINE_PARCEL_WRITER(Int64,jlong)
+DEFINE_PARCEL_WRITER(Float,jfloat)
+DEFINE_PARCEL_WRITER(Double,jdouble)
+
+DEFINE_PARCEL_READER(Int8,jbyte)
+DEFINE_PARCEL_READER(Int16,jshort)
+DEFINE_PARCEL_READER(Int32,jint)
+DEFINE_PARCEL_READER(Int64,jlong)
+DEFINE_PARCEL_READER(Float,jfloat)
+DEFINE_PARCEL_READER(Double,jdouble)
+
+static jboolean JHwParcel_native_readBool(JNIEnv *env, jobject thiz) {
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    bool val;
+    status_t err = parcel->readBool(&val);
+    signalExceptionForError(env, err);
+
+    return (jboolean)val;
+}
+
+static void JHwParcel_native_writeStatus(
+        JNIEnv *env, jobject thiz, jint statusCode) {
+    using hardware::Status;
+
+    Status status;
+    switch (statusCode) {
+        case 0:  // kStatusSuccess
+            status = Status::ok();
+            break;
+        case -1:  // kStatusError
+            status = Status::fromStatusT(UNKNOWN_ERROR);
+            break;
+        default:
+            CHECK(!"Should not be here");
+    }
+
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    status_t err = status.writeToParcel(parcel);
+    signalExceptionForError(env, err);
+}
+
+static void JHwParcel_native_verifySuccess(JNIEnv *env, jobject thiz) {
+    using hardware::Status;
+
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    Status status;
+    status_t err = status.readFromParcel(*parcel);
+    signalExceptionForError(env, err);
+}
+
+static void JHwParcel_native_releaseTemporaryStorage(
+        JNIEnv *env, jobject thiz) {
+    JHwParcel::GetNativeContext(env, thiz)->getStorage()->release(env);
+}
+
+static void JHwParcel_native_send(JNIEnv *env, jobject thiz) {
+    JHwParcel::GetNativeContext(env, thiz)->send();
+}
+
+static void JHwParcel_native_writeString(
+        JNIEnv *env, jobject thiz, jstring valObj) {
+    if (valObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+
+    sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz);
+
+    const hidl_string *s =
+        impl->getStorage()->allocTemporaryString(env, valObj);
+
+    hardware::Parcel *parcel = impl->getParcel();
+
+    size_t parentHandle;
+    status_t err = parcel->writeBuffer(s, sizeof(*s), &parentHandle);
+
+    if (err == OK) {
+        err = s->writeEmbeddedToParcel(
+                parcel, parentHandle, 0 /* parentOffset */);
+    }
+
+    signalExceptionForError(env, err);
+}
+
+#define DEFINE_PARCEL_ARRAY_WRITER(Suffix,Type)                                \
+static void JHwParcel_native_write ## Suffix ## Array(                         \
+        JNIEnv *env, jobject thiz, jint size, Type ## Array valObj) {          \
+    if (valObj == NULL) {                                                      \
+        jniThrowException(env, "java/lang/NullPointerException", NULL);        \
+        return;                                                                \
+    }                                                                          \
+                                                                               \
+    jsize len = env->GetArrayLength(valObj);                                   \
+                                                                               \
+    if (len != size) {                                                         \
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);    \
+        return;                                                                \
+    }                                                                          \
+                                                                               \
+    sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz);               \
+                                                                               \
+    const Type *val =                                                          \
+        impl->getStorage()->allocTemporary ## Suffix ## Array(env, valObj);    \
+                                                                               \
+    hardware::Parcel *parcel = impl->getParcel();                              \
+                                                                               \
+    size_t parentHandle;                                                       \
+    status_t err = parcel->writeBuffer(                                        \
+            val, size * sizeof(*val), &parentHandle);                          \
+                                                                               \
+    signalExceptionForError(env, err);                                         \
+}
+
+#define DEFINE_PARCEL_VECTOR_WRITER(Suffix,Type)                               \
+static void JHwParcel_native_write ## Suffix ## Vector(                        \
+        JNIEnv *env, jobject thiz, Type ## Array valObj) {                     \
+    if (valObj == NULL) {                                                      \
+        jniThrowException(env, "java/lang/NullPointerException", NULL);        \
+        return;                                                                \
+    }                                                                          \
+                                                                               \
+    sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz);               \
+                                                                               \
+    const hidl_vec<Type> *vec =                                                \
+        impl->getStorage()->allocTemporary ## Suffix ## Vector(env, valObj);   \
+                                                                               \
+    hardware::Parcel *parcel = impl->getParcel();                              \
+                                                                               \
+    size_t parentHandle;                                                       \
+    status_t err = parcel->writeBuffer(vec, sizeof(*vec), &parentHandle);      \
+                                                                               \
+    if (err == OK) {                                                           \
+        size_t childHandle;                                                    \
+                                                                               \
+        err = vec->writeEmbeddedToParcel(                                      \
+                parcel,                                                        \
+                parentHandle,                                                  \
+                0 /* parentOffset */,                                          \
+                &childHandle);                                                 \
+    }                                                                          \
+                                                                               \
+    signalExceptionForError(env, err);                                         \
+}
+
+DEFINE_PARCEL_ARRAY_WRITER(Int8,jbyte)
+DEFINE_PARCEL_ARRAY_WRITER(Int16,jshort)
+DEFINE_PARCEL_ARRAY_WRITER(Int32,jint)
+DEFINE_PARCEL_ARRAY_WRITER(Int64,jlong)
+DEFINE_PARCEL_ARRAY_WRITER(Float,jfloat)
+DEFINE_PARCEL_ARRAY_WRITER(Double,jdouble)
+
+DEFINE_PARCEL_VECTOR_WRITER(Int8,jbyte)
+DEFINE_PARCEL_VECTOR_WRITER(Int16,jshort)
+DEFINE_PARCEL_VECTOR_WRITER(Int32,jint)
+DEFINE_PARCEL_VECTOR_WRITER(Int64,jlong)
+DEFINE_PARCEL_VECTOR_WRITER(Float,jfloat)
+DEFINE_PARCEL_VECTOR_WRITER(Double,jdouble)
+
+static void JHwParcel_native_writeBoolArray(
+        JNIEnv *env, jobject thiz, jint size, jbooleanArray valObj) {
+    if (valObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+
+    jsize len = env->GetArrayLength(valObj);
+
+    if (len != size) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+
+    sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz);
+
+    jboolean *src = env->GetBooleanArrayElements(valObj, nullptr);
+
+    bool *dst =
+        (bool *)impl->getStorage()->allocTemporaryStorage(size * sizeof(bool));
+
+    for (jint i = 0; i < size; ++i) {
+        dst[i] = src[i];
+    }
+
+    env->ReleaseBooleanArrayElements(valObj, src, 0 /* mode */);
+    src = nullptr;
+
+    hardware::Parcel *parcel = impl->getParcel();
+
+    size_t parentHandle;
+    status_t err = parcel->writeBuffer(
+            dst, size * sizeof(*dst), &parentHandle);
+
+    signalExceptionForError(env, err);
+}
+
+static void JHwParcel_native_writeBoolVector(
+        JNIEnv *env, jobject thiz, jbooleanArray valObj) {
+    if (valObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+
+    sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz);
+
+    void *vecPtr =
+        impl->getStorage()->allocTemporaryStorage(sizeof(hidl_vec<bool>));
+
+    hidl_vec<bool> *vec = new (vecPtr) hidl_vec<bool>;
+
+    jsize len = env->GetArrayLength(valObj);
+
+    jboolean *src = env->GetBooleanArrayElements(valObj, nullptr);
+
+    bool *dst =
+        (bool *)impl->getStorage()->allocTemporaryStorage(len * sizeof(bool));
+
+    for (jsize i = 0; i < len; ++i) {
+        dst[i] = src[i];
+    }
+
+    env->ReleaseBooleanArrayElements(valObj, src, 0 /* mode */);
+    src = nullptr;
+
+    vec->setToExternal(dst, len);
+
+    hardware::Parcel *parcel = impl->getParcel();
+
+    size_t parentHandle;
+    status_t err = parcel->writeBuffer(vec, sizeof(*vec), &parentHandle);
+
+    if (err == OK) {
+        size_t childHandle;
+
+        err = vec->writeEmbeddedToParcel(
+                parcel,
+                parentHandle,
+                0 /* parentOffset */,
+                &childHandle);
+    }
+
+    signalExceptionForError(env, err);
+}
+
+static void JHwParcel_native_writeStrongBinder(
+        JNIEnv *env, jobject thiz, jobject binderObj) {
+    sp<hardware::IBinder> binder;
+    if (binderObj != NULL) {
+        ScopedLocalRef<jclass> hwBinderKlass(
+                env, FindClassOrDie(env, PACKAGE_PATH "/HwBinder"));
+
+        ScopedLocalRef<jclass> hwRemoteBinderKlass(
+                env, FindClassOrDie(env, PACKAGE_PATH "/HwRemoteBinder"));
+
+        if (env->IsInstanceOf(binderObj, hwBinderKlass.get())) {
+            binder = JHwBinder::GetNativeContext(env, binderObj);
+        } else if (env->IsInstanceOf(binderObj, hwRemoteBinderKlass.get())) {
+            binder = JHwRemoteBinder::GetNativeContext(
+                    env, binderObj)->getBinder();
+        } else {
+            signalExceptionForError(env, INVALID_OPERATION);
+            return;
+        }
+    }
+
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    status_t err = parcel->writeStrongBinder(binder);
+    signalExceptionForError(env, err);
+}
+
+static jstring MakeStringObjFromHidlString(JNIEnv *env, const hidl_string &s) {
+    String16 utf16String(s.c_str(), s.size());
+
+    return env->NewString(
+            reinterpret_cast<const jchar *>(utf16String.string()),
+            utf16String.size());
+}
+
+static jstring JHwParcel_native_readString(JNIEnv *env, jobject thiz) {
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    size_t parentHandle;
+
+    const hidl_string *s = static_cast<const hidl_string *>(
+            parcel->readBuffer(&parentHandle));
+
+    if (s == NULL) {
+        signalExceptionForError(env, UNKNOWN_ERROR);
+        return NULL;
+    }
+
+    status_t err = const_cast<hidl_string *>(s)->readEmbeddedFromParcel(
+            *parcel, parentHandle, 0 /* parentOffset */);
+
+    if (err != OK) {
+        signalExceptionForError(env, err);
+        return NULL;
+    }
+
+    return MakeStringObjFromHidlString(env, *s);
+}
+
+#define DEFINE_PARCEL_ARRAY_READER(Suffix,Type,NewType)                        \
+static Type ## Array JHwParcel_native_read ## Suffix ## Array(                 \
+        JNIEnv *env, jobject thiz, jint size) {                                \
+    hardware::Parcel *parcel =                                                 \
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();                   \
+                                                                               \
+    size_t parentHandle;                                                       \
+    const Type *val = static_cast<const Type *>(                               \
+            parcel->readBuffer(&parentHandle));                                \
+                                                                               \
+    Type ## Array valObj = env->New ## NewType ## Array(size);                 \
+    env->Set ## NewType ## ArrayRegion(valObj, 0, size, val);                  \
+                                                                               \
+    return valObj;                                                             \
+}
+
+#define DEFINE_PARCEL_VECTOR_READER(Suffix,Type,NewType)                       \
+static Type ## Array JHwParcel_native_read ## Suffix ## Vector(                \
+        JNIEnv *env, jobject thiz) {                                           \
+    hardware::Parcel *parcel =                                                 \
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();                   \
+                                                                               \
+    size_t parentHandle;                                                       \
+                                                                               \
+    const hidl_vec<Type> *vec =                                                \
+        (const hidl_vec<Type> *)parcel->readBuffer(&parentHandle);             \
+                                                                               \
+    if (vec == NULL) {                                                         \
+        signalExceptionForError(env, UNKNOWN_ERROR);                           \
+        return NULL;                                                           \
+    }                                                                          \
+                                                                               \
+    size_t childHandle;                                                        \
+                                                                               \
+    status_t err = const_cast<hidl_vec<Type> *>(vec)                           \
+        ->readEmbeddedFromParcel(                                              \
+                *parcel,                                                       \
+                parentHandle,                                                  \
+                0 /* parentOffset */,                                          \
+                &childHandle);                                                 \
+                                                                               \
+    if (err != OK) {                                                           \
+        signalExceptionForError(env, err);                                     \
+        return NULL;                                                           \
+    }                                                                          \
+                                                                               \
+    Type ## Array valObj = env->New ## NewType ## Array(vec->size());          \
+    env->Set ## NewType ## ArrayRegion(valObj, 0, vec->size(), &(*vec)[0]);    \
+                                                                               \
+    return valObj;                                                             \
+}
+
+DEFINE_PARCEL_ARRAY_READER(Int8,jbyte,Byte)
+DEFINE_PARCEL_ARRAY_READER(Int16,jshort,Short)
+DEFINE_PARCEL_ARRAY_READER(Int32,jint,Int)
+DEFINE_PARCEL_ARRAY_READER(Int64,jlong,Long)
+DEFINE_PARCEL_ARRAY_READER(Float,jfloat,Float)
+DEFINE_PARCEL_ARRAY_READER(Double,jdouble,Double)
+
+DEFINE_PARCEL_VECTOR_READER(Int8,jbyte,Byte)
+DEFINE_PARCEL_VECTOR_READER(Int16,jshort,Short)
+DEFINE_PARCEL_VECTOR_READER(Int32,jint,Int)
+DEFINE_PARCEL_VECTOR_READER(Int64,jlong,Long)
+DEFINE_PARCEL_VECTOR_READER(Float,jfloat,Float)
+DEFINE_PARCEL_VECTOR_READER(Double,jdouble,Double)
+
+static jbooleanArray JHwParcel_native_readBoolArray(
+        JNIEnv *env, jobject thiz, jint size) {
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    size_t parentHandle;
+    const bool *val = static_cast<const bool *>(
+            parcel->readBuffer(&parentHandle));
+
+    jbooleanArray valObj = env->NewBooleanArray(size);
+
+    for (jint i = 0; i < size; ++i) {
+        jboolean x = val[i];
+        env->SetBooleanArrayRegion(valObj, i, 1, &x);
+    }
+
+    return valObj;
+}
+
+static jbooleanArray JHwParcel_native_readBoolVector(
+        JNIEnv *env, jobject thiz) {
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    size_t parentHandle;
+
+    const hidl_vec<bool> *vec =
+        (const hidl_vec<bool> *)parcel->readBuffer(&parentHandle);
+
+    if (vec == NULL) {
+        signalExceptionForError(env, UNKNOWN_ERROR);
+        return NULL;
+    }
+
+    size_t childHandle;
+
+    status_t err = const_cast<hidl_vec<bool> *>(vec)
+        ->readEmbeddedFromParcel(
+                *parcel,
+                parentHandle,
+                0 /* parentOffset */,
+                &childHandle);
+
+    if (err != OK) {
+        signalExceptionForError(env, err);
+        return NULL;
+    }
+
+    jbooleanArray valObj = env->NewBooleanArray(vec->size());
+
+    for (size_t i = 0; i < vec->size(); ++i) {
+        jboolean x = (*vec)[i];
+        env->SetBooleanArrayRegion(valObj, i, 1, &x);
+    }
+
+    return valObj;
+}
+
+static jobjectArray MakeStringArray(
+        JNIEnv *env, const hidl_string *array, size_t size) {
+    ScopedLocalRef<jclass> stringKlass(
+            env,
+            env->FindClass("java/lang/String"));
+
+    // XXX Why can't I use ScopedLocalRef<> for the arrayObj and the stringObjs?
+
+    jobjectArray arrayObj = env->NewObjectArray(size, stringKlass.get(), NULL);
+
+    for (size_t i = 0; i < size; ++i) {
+        jstring stringObj = MakeStringObjFromHidlString(env, array[i]);
+
+        env->SetObjectArrayElement(
+                arrayObj,
+                i,
+                stringObj);
+    }
+
+    return arrayObj;
+}
+
+static jobjectArray JHwParcel_native_readStringArray(
+        JNIEnv *env, jobject thiz, jint size) {
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    size_t parentHandle;
+    const hidl_string *val = static_cast<const hidl_string *>(
+            parcel->readBuffer(&parentHandle));
+
+    if (val == NULL) {
+        signalExceptionForError(env, UNKNOWN_ERROR);
+        return NULL;
+    }
+
+    status_t err = OK;
+    for (jint i = 0; (err == OK) && (i < size); ++i) {
+        err = const_cast<hidl_string *>(&val[i])
+            ->readEmbeddedFromParcel(
+                    *parcel,
+                    parentHandle,
+                    i * sizeof(hidl_string));
+    }
+
+    if (err != OK) {
+        signalExceptionForError(env, err);
+        return NULL;
+    }
+
+    return MakeStringArray(env, val, size);
+}
+
+static void JHwParcel_native_writeStringArray(
+        JNIEnv *env, jobject thiz, jint size, jobjectArray arrayObj) {
+    if (arrayObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+
+    jsize len = env->GetArrayLength(arrayObj);
+
+    if (len != size) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+
+    sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz);
+
+    hidl_string *strings = impl->getStorage()->allocStringArray(len);
+
+    for (jsize i = 0; i < len; ++i) {
+        ScopedLocalRef<jstring> stringObj(
+                env,
+                (jstring)env->GetObjectArrayElement(arrayObj, i));
+
+        const hidl_string *s =
+            impl->getStorage()->allocTemporaryString(env, stringObj.get());
+
+        strings[i].setToExternal(s->c_str(), s->size());
+    }
+
+    hardware::Parcel *parcel = impl->getParcel();
+
+    size_t parentHandle;
+    status_t err = parcel->writeBuffer(
+            strings, sizeof(hidl_string) * len, &parentHandle);
+
+    for (jsize i = 0; (err == OK) && (i < len); ++i) {
+        err = strings[i].writeEmbeddedToParcel(
+                parcel, parentHandle, i * sizeof(hidl_string));
+    }
+
+    signalExceptionForError(env, err);
+}
+
+static jobjectArray JHwParcel_native_readStringVector(
+        JNIEnv *env, jobject thiz) {
+    typedef hidl_vec<hidl_string> string_vec;
+
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    size_t parentHandle;
+
+    const string_vec *vec=
+        (const string_vec *)parcel->readBuffer(&parentHandle);
+
+    if (vec == NULL) {
+        signalExceptionForError(env, UNKNOWN_ERROR);
+        return NULL;
+    }
+
+    size_t childHandle;
+    status_t err = const_cast<string_vec *>(vec)->readEmbeddedFromParcel(
+            *parcel, parentHandle, 0 /* parentOffset */, &childHandle);
+
+    for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) {
+        err = const_cast<hidl_vec<hidl_string> *>(vec)
+            ->readEmbeddedFromParcel(
+                    *parcel,
+                    childHandle,
+                    i * sizeof(hidl_string),
+                    nullptr /* childHandle */);
+    }
+
+    if (err != OK) {
+        signalExceptionForError(env, err);
+        return NULL;
+    }
+
+    return MakeStringArray(env, &(*vec)[0], vec->size());
+}
+
+static void JHwParcel_native_writeStringVector(
+        JNIEnv *env, jobject thiz, jobjectArray arrayObj) {
+    typedef hidl_vec<hidl_string> string_vec;
+
+    if (arrayObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+
+    jsize len = env->GetArrayLength(arrayObj);
+
+    sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz);
+
+    void *vecPtr =
+        impl->getStorage()->allocTemporaryStorage(sizeof(string_vec));
+
+    string_vec *vec = new (vecPtr) string_vec;
+
+    hidl_string *strings = impl->getStorage()->allocStringArray(len);
+    vec->setToExternal(strings, len);
+
+    for (jsize i = 0; i < len; ++i) {
+        ScopedLocalRef<jstring> stringObj(
+                env,
+                (jstring)env->GetObjectArrayElement(arrayObj, i));
+
+        const hidl_string *s =
+            impl->getStorage()->allocTemporaryString(env, stringObj.get());
+
+        strings[i].setToExternal(s->c_str(), s->size());
+    }
+
+    hardware::Parcel *parcel = impl->getParcel();
+
+    size_t parentHandle;
+    status_t err = parcel->writeBuffer(vec, sizeof(*vec), &parentHandle);
+
+    if (err == OK) {
+        size_t childHandle;
+        err = vec->writeEmbeddedToParcel(
+                parcel,
+                parentHandle,
+                0 /* parentOffset */,
+                &childHandle);
+
+        for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) {
+            err = (*vec)[i].writeEmbeddedToParcel(
+                    parcel,
+                    childHandle,
+                    i * sizeof(hidl_string));
+        }
+    }
+
+    signalExceptionForError(env, err);
+}
+
+static jobject JHwParcel_native_readStrongBinder(JNIEnv *env, jobject thiz) {
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    sp<hardware::IBinder> binder = parcel->readStrongBinder();
+
+    if (binder == NULL) {
+        return NULL;
+    }
+
+    return JHwRemoteBinder::NewObject(env, binder);
+}
+
+static jobject JHwParcel_native_readBuffer(JNIEnv *env, jobject thiz) {
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    size_t handle;
+    const void *ptr = parcel->readBuffer(&handle);
+
+    if (ptr == nullptr) {
+        jniThrowException(env, "java/util/NoSuchElementException", NULL);
+        return nullptr;
+    }
+
+    return JHwBlob::NewObject(env, ptr, handle);
+}
+
+static jobject JHwParcel_native_readEmbeddedBuffer(
+        JNIEnv *env, jobject thiz, jlong parentHandle, jlong offset) {
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    size_t childHandle;
+
+    const void *ptr =
+        parcel->readEmbeddedBuffer(&childHandle, parentHandle, offset);
+
+    if (ptr == nullptr) {
+        jniThrowException(env, "java/util/NoSuchElementException", NULL);
+        return 0;
+    }
+
+    return JHwBlob::NewObject(env, ptr, childHandle);
+}
+
+static void JHwParcel_native_writeBuffer(
+        JNIEnv *env, jobject thiz, jobject blobObj) {
+    if (blobObj == nullptr) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+
+    hardware::Parcel *parcel =
+        JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, blobObj);
+    status_t err = blob->writeToParcel(parcel);
+
+    if (err != OK) {
+        signalExceptionForError(env, err);
+    }
+}
+
+static JNINativeMethod gMethods[] = {
+    { "native_init", "()J", (void *)JHwParcel_native_init },
+    { "native_setup", "(Z)V", (void *)JHwParcel_native_setup },
+
+    { "writeInterfaceToken", "(Ljava/lang/String;)V",
+        (void *)JHwParcel_native_writeInterfaceToken },
+
+    { "writeBool", "(Z)V", (void *)JHwParcel_native_writeBool },
+    { "writeInt8", "(B)V", (void *)JHwParcel_native_writeInt8 },
+    { "writeInt16", "(S)V", (void *)JHwParcel_native_writeInt16 },
+    { "writeInt32", "(I)V", (void *)JHwParcel_native_writeInt32 },
+    { "writeInt64", "(J)V", (void *)JHwParcel_native_writeInt64 },
+    { "writeFloat", "(F)V", (void *)JHwParcel_native_writeFloat },
+    { "writeDouble", "(D)V", (void *)JHwParcel_native_writeDouble },
+
+    { "writeString", "(Ljava/lang/String;)V",
+        (void *)JHwParcel_native_writeString },
+
+    { "writeBoolArray", "(I[Z)V", (void *)JHwParcel_native_writeBoolArray },
+    { "writeBoolVector", "([Z)V", (void *)JHwParcel_native_writeBoolVector },
+    { "writeInt8Array", "(I[B)V", (void *)JHwParcel_native_writeInt8Array },
+    { "writeInt8Vector", "([B)V", (void *)JHwParcel_native_writeInt8Vector },
+    { "writeInt16Array", "(I[S)V", (void *)JHwParcel_native_writeInt16Array },
+    { "writeInt16Vector", "([S)V", (void *)JHwParcel_native_writeInt16Vector },
+    { "writeInt32Array", "(I[I)V", (void *)JHwParcel_native_writeInt32Array },
+    { "writeInt32Vector", "([I)V", (void *)JHwParcel_native_writeInt32Vector },
+    { "writeInt64Array", "(I[J)V", (void *)JHwParcel_native_writeInt64Array },
+    { "writeInt64Vector", "([J)V", (void *)JHwParcel_native_writeInt64Vector },
+    { "writeFloatArray", "(I[F)V", (void *)JHwParcel_native_writeFloatArray },
+    { "writeFloatVector", "([F)V", (void *)JHwParcel_native_writeFloatVector },
+    { "writeDoubleArray", "(I[D)V", (void *)JHwParcel_native_writeDoubleArray },
+
+    { "writeDoubleVector", "([D)V",
+        (void *)JHwParcel_native_writeDoubleVector },
+
+    { "writeStringArray", "(I[Ljava/lang/String;)V",
+        (void *)JHwParcel_native_writeStringArray },
+
+    { "writeStringVector", "([Ljava/lang/String;)V",
+        (void *)JHwParcel_native_writeStringVector },
+
+    { "writeStrongBinder", "(L" PACKAGE_PATH "/IHwBinder;)V",
+        (void *)JHwParcel_native_writeStrongBinder },
+
+    { "enforceInterface", "(Ljava/lang/String;)V",
+        (void *)JHwParcel_native_enforceInterface },
+
+    { "readBool", "()Z", (void *)JHwParcel_native_readBool },
+    { "readInt8", "()B", (void *)JHwParcel_native_readInt8 },
+    { "readInt16", "()S", (void *)JHwParcel_native_readInt16 },
+    { "readInt32", "()I", (void *)JHwParcel_native_readInt32 },
+    { "readInt64", "()J", (void *)JHwParcel_native_readInt64 },
+    { "readFloat", "()F", (void *)JHwParcel_native_readFloat },
+    { "readDouble", "()D", (void *)JHwParcel_native_readDouble },
+
+    { "readString", "()Ljava/lang/String;",
+        (void *)JHwParcel_native_readString },
+
+    { "readBoolArray", "(I)[Z", (void *)JHwParcel_native_readBoolArray },
+    { "readBoolVector", "()[Z", (void *)JHwParcel_native_readBoolVector },
+    { "readInt8Array", "(I)[B", (void *)JHwParcel_native_readInt8Array },
+    { "readInt8Vector", "()[B", (void *)JHwParcel_native_readInt8Vector },
+    { "readInt16Array", "(I)[S", (void *)JHwParcel_native_readInt16Array },
+    { "readInt16Vector", "()[S", (void *)JHwParcel_native_readInt16Vector },
+    { "readInt32Array", "(I)[I", (void *)JHwParcel_native_readInt32Array },
+    { "readInt32Vector", "()[I", (void *)JHwParcel_native_readInt32Vector },
+    { "readInt64Array", "(I)[J", (void *)JHwParcel_native_readInt64Array },
+    { "readInt64Vector", "()[J", (void *)JHwParcel_native_readInt64Vector },
+    { "readFloatArray", "(I)[F", (void *)JHwParcel_native_readFloatArray },
+    { "readFloatVector", "()[F", (void *)JHwParcel_native_readFloatVector },
+    { "readDoubleArray", "(I)[D", (void *)JHwParcel_native_readDoubleArray },
+    { "readDoubleVector", "()[D", (void *)JHwParcel_native_readDoubleVector },
+
+    { "readStringArray", "(I)[Ljava/lang/String;",
+        (void *)JHwParcel_native_readStringArray },
+
+    { "readStringVector", "()[Ljava/lang/String;",
+        (void *)JHwParcel_native_readStringVector },
+
+    { "readStrongBinder", "()L" PACKAGE_PATH "/IHwBinder;",
+        (void *)JHwParcel_native_readStrongBinder },
+
+    { "writeStatus", "(I)V", (void *)JHwParcel_native_writeStatus },
+
+    { "verifySuccess", "()V", (void *)JHwParcel_native_verifySuccess },
+
+    { "releaseTemporaryStorage", "()V",
+        (void *)JHwParcel_native_releaseTemporaryStorage },
+
+    { "send", "()V", (void *)JHwParcel_native_send },
+
+    { "readBuffer", "()L" PACKAGE_PATH "/HwBlob;",
+        (void *)JHwParcel_native_readBuffer },
+
+    { "readEmbeddedBuffer", "(JJ)L" PACKAGE_PATH "/HwBlob;",
+        (void *)JHwParcel_native_readEmbeddedBuffer },
+
+    { "writeBuffer", "(L" PACKAGE_PATH "/HwBlob;)V",
+        (void *)JHwParcel_native_writeBuffer },
+};
+
+namespace android {
+
+int register_android_os_HwParcel(JNIEnv *env) {
+    return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
+}
+
+}  // namespace android
diff --git a/core/jni/android_os_HwParcel.h b/core/jni/android_os_HwParcel.h
new file mode 100644
index 0000000..708bbba
--- /dev/null
+++ b/core/jni/android_os_HwParcel.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_OS_HW_PARCEL_H
+#define ANDROID_OS_HW_PARCEL_H
+
+#include "hwbinder/EphemeralStorage.h"
+
+#include <android-base/macros.h>
+#include <hwbinder/IBinder.h>
+#include <hwbinder/Parcel.h>
+#include <jni.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct JHwParcel : public RefBase {
+    static void InitClass(JNIEnv *env);
+
+    static sp<JHwParcel> SetNativeContext(
+            JNIEnv *env, jobject thiz, const sp<JHwParcel> &context);
+
+    static sp<JHwParcel> GetNativeContext(JNIEnv *env, jobject thiz);
+
+    static jobject NewObject(JNIEnv *env);
+
+    JHwParcel(JNIEnv *env, jobject thiz);
+
+    void setParcel(hardware::Parcel *parcel, bool assumeOwnership);
+    hardware::Parcel *getParcel();
+
+    EphemeralStorage *getStorage();
+
+    void setTransactCallback(::android::hardware::IBinder::TransactCallback cb);
+
+    void send();
+    bool wasSent() const;
+
+protected:
+    virtual ~JHwParcel();
+
+private:
+    jclass mClass;
+    jobject mObject;
+
+    hardware::Parcel *mParcel;
+    bool mOwnsParcel;
+
+    EphemeralStorage mStorage;
+
+    ::android::hardware::IBinder::TransactCallback mTransactCallback;
+    bool mWasSent;
+
+    DISALLOW_COPY_AND_ASSIGN(JHwParcel);
+};
+
+void signalExceptionForError(JNIEnv *env, status_t err);
+int register_android_os_HwParcel(JNIEnv *env);
+
+}  // namespace android
+
+#endif  // ANDROID_OS_HW_PARCEL_H
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
new file mode 100644
index 0000000..3023ba8
--- /dev/null
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "JHwRemoteBinder"
+#include <android-base/logging.h>
+
+#include "android_os_HwRemoteBinder.h"
+
+#include "android_os_HwParcel.h"
+
+#include <JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <hidl/IServiceManager.h>
+#include <hidl/Status.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+#include "core_jni_helpers.h"
+
+using android::AndroidRuntime;
+
+#define PACKAGE_PATH    "android/os"
+#define CLASS_NAME      "HwRemoteBinder"
+#define CLASS_PATH      PACKAGE_PATH "/" CLASS_NAME
+
+namespace android {
+
+static struct fields_t {
+    jfieldID contextID;
+    jmethodID constructID;
+
+} gFields;
+
+// static
+void JHwRemoteBinder::InitClass(JNIEnv *env) {
+    ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
+
+    gFields.contextID =
+        GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J");
+
+    gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V");
+}
+
+// static
+sp<JHwRemoteBinder> JHwRemoteBinder::SetNativeContext(
+        JNIEnv *env, jobject thiz, const sp<JHwRemoteBinder> &context) {
+    sp<JHwRemoteBinder> old =
+        (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID);
+
+    if (context != NULL) {
+        context->incStrong(NULL /* id */);
+    }
+
+    if (old != NULL) {
+        old->decStrong(NULL /* id */);
+    }
+
+    env->SetLongField(thiz, gFields.contextID, (long)context.get());
+
+    return old;
+}
+
+// static
+sp<JHwRemoteBinder> JHwRemoteBinder::GetNativeContext(
+        JNIEnv *env, jobject thiz) {
+    return (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID);
+}
+
+// static
+jobject JHwRemoteBinder::NewObject(
+        JNIEnv *env, const sp<hardware::IBinder> &binder) {
+    ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
+
+    // XXX Have to look up the constructor here because otherwise that static
+    // class initializer isn't called and gFields.constructID is undefined :(
+
+    jmethodID constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V");
+
+    jobject obj = env->NewObject(clazz.get(), constructID);
+    JHwRemoteBinder::GetNativeContext(env, obj)->setBinder(binder);
+
+    return obj;
+}
+
+JHwRemoteBinder::JHwRemoteBinder(
+        JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder)
+    : mBinder(binder) {
+    jclass clazz = env->GetObjectClass(thiz);
+    CHECK(clazz != NULL);
+
+    mClass = (jclass)env->NewGlobalRef(clazz);
+    mObject = env->NewWeakGlobalRef(thiz);
+}
+
+JHwRemoteBinder::~JHwRemoteBinder() {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    env->DeleteWeakGlobalRef(mObject);
+    mObject = NULL;
+
+    env->DeleteGlobalRef(mClass);
+    mClass = NULL;
+}
+
+sp<hardware::IBinder> JHwRemoteBinder::getBinder() {
+    return mBinder;
+}
+
+void JHwRemoteBinder::setBinder(const sp<hardware::IBinder> &binder) {
+    mBinder = binder;
+}
+
+}  // namespace android
+
+////////////////////////////////////////////////////////////////////////////////
+
+using namespace android;
+
+static void releaseNativeContext(void *nativeContext) {
+    sp<JHwRemoteBinder> binder = (JHwRemoteBinder *)nativeContext;
+
+    if (binder != NULL) {
+        binder->decStrong(NULL /* id */);
+    }
+}
+
+static jlong JHwRemoteBinder_native_init(JNIEnv *env) {
+    JHwRemoteBinder::InitClass(env);
+
+    return reinterpret_cast<jlong>(&releaseNativeContext);
+}
+
+static void JHwRemoteBinder_native_setup_empty(JNIEnv *env, jobject thiz) {
+    sp<JHwRemoteBinder> context =
+        new JHwRemoteBinder(env, thiz, NULL /* service */);
+
+    JHwRemoteBinder::SetNativeContext(env, thiz, context);
+}
+
+static void JHwRemoteBinder_native_transact(
+        JNIEnv *env,
+        jobject thiz,
+        jint code,
+        jobject requestObj,
+        jobject replyObj,
+        jint flags) {
+    sp<hardware::IBinder> binder =
+        JHwRemoteBinder::GetNativeContext(env, thiz)->getBinder();
+
+    if (requestObj == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
+
+    const hardware::Parcel *request =
+        JHwParcel::GetNativeContext(env, requestObj)->getParcel();
+
+    hardware::Parcel *reply =
+        JHwParcel::GetNativeContext(env, replyObj)->getParcel();
+
+    status_t err = binder->transact(code, *request, reply, flags);
+    signalExceptionForError(env, err);
+}
+
+static JNINativeMethod gMethods[] = {
+    { "native_init", "()J", (void *)JHwRemoteBinder_native_init },
+
+    { "native_setup_empty", "()V",
+        (void *)JHwRemoteBinder_native_setup_empty },
+
+    { "transact",
+        "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V",
+        (void *)JHwRemoteBinder_native_transact },
+};
+
+namespace android {
+
+int register_android_os_HwRemoteBinder(JNIEnv *env) {
+    return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
+}
+
+}  // namespace android
+
diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h
new file mode 100644
index 0000000..fd33338
--- /dev/null
+++ b/core/jni/android_os_HwRemoteBinder.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_OS_HW_REMOTE_BINDER_H
+#define ANDROID_OS_HW_REMOTE_BINDER_H
+
+#include <android-base/macros.h>
+#include <hwbinder/Binder.h>
+#include <jni.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct JHwRemoteBinder : public RefBase {
+    static void InitClass(JNIEnv *env);
+
+    static sp<JHwRemoteBinder> SetNativeContext(
+            JNIEnv *env, jobject thiz, const sp<JHwRemoteBinder> &context);
+
+    static sp<JHwRemoteBinder> GetNativeContext(JNIEnv *env, jobject thiz);
+
+    static jobject NewObject(JNIEnv *env, const sp<hardware::IBinder> &binder);
+
+    JHwRemoteBinder(
+            JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder);
+
+    sp<hardware::IBinder> getBinder();
+    void setBinder(const sp<hardware::IBinder> &binder);
+
+protected:
+    virtual ~JHwRemoteBinder();
+
+private:
+    jclass mClass;
+    jobject mObject;
+
+    sp<hardware::IBinder> mBinder;
+
+    DISALLOW_COPY_AND_ASSIGN(JHwRemoteBinder);
+};
+
+int register_android_os_HwRemoteBinder(JNIEnv *env);
+
+}  // namespace android
+
+#endif  // ANDROID_OS_HW_REMOTE_BINDER_H
+
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 8ba77ae..4b68c0d 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -23,9 +23,9 @@
 #include "selinux/selinux.h"
 #include "selinux/android.h"
 #include <errno.h>
+#include <memory>
 #include <ScopedLocalRef.h>
 #include <ScopedUtfChars.h>
-#include <UniquePtr.h>
 
 namespace android {
 
@@ -34,7 +34,7 @@
         freecon(p);
     }
 };
-typedef UniquePtr<char[], SecurityContext_Delete> Unique_SecurityContext;
+typedef std::unique_ptr<char[], SecurityContext_Delete> Unique_SecurityContext;
 
 static jboolean isSELinuxDisabled = true;
 
@@ -112,7 +112,7 @@
         return false;
     }
 
-    UniquePtr<ScopedUtfChars> context;
+    std::unique_ptr<ScopedUtfChars> context;
     const char* context_c_str = NULL;
     if (contextStr != NULL) {
         context.reset(new ScopedUtfChars(env, contextStr));
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 364ac44..7e01657 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -20,7 +20,6 @@
 #include "core_jni_helpers.h"
 
 #include <ScopedUtfChars.h>
-#include <UniquePtr.h>
 #include <androidfw/ZipFileRO.h>
 #include <androidfw/ZipUtils.h>
 #include <utils/Log.h>
@@ -37,6 +36,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <memory>
 
 #define APK_LIB "lib/"
 #define APK_LIB_LEN (sizeof(APK_LIB) - 1)
@@ -398,7 +398,7 @@
         return INSTALL_FAILED_INVALID_APK;
     }
 
-    UniquePtr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
+    std::unique_ptr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
     if (it.get() == NULL) {
         return INSTALL_FAILED_INVALID_APK;
     }
@@ -446,7 +446,7 @@
         return INSTALL_FAILED_INVALID_APK;
     }
 
-    UniquePtr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
+    std::unique_ptr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
     if (it.get() == NULL) {
         return INSTALL_FAILED_INVALID_APK;
     }
diff --git a/core/jni/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp
new file mode 100644
index 0000000..187beee
--- /dev/null
+++ b/core/jni/hwbinder/EphemeralStorage.cpp
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "EphemeralStorage"
+//#define LOG_NDEBUG 0
+
+#include <android-base/logging.h>
+
+#include "EphemeralStorage.h"
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+namespace android {
+
+EphemeralStorage::EphemeralStorage() {
+}
+
+EphemeralStorage::~EphemeralStorage() {
+    CHECK(mItems.empty())
+        << "All item storage should have been released by now.";
+}
+
+hidl_string *EphemeralStorage::allocStringArray(size_t size) {
+    Item item;
+    item.mType = TYPE_STRING_ARRAY;
+    item.mObj = NULL;
+    item.mPtr = new hidl_string[size];
+    mItems.push_back(item);
+
+    return static_cast<hidl_string *>(item.mPtr);
+}
+
+void *EphemeralStorage::allocTemporaryStorage(size_t size) {
+    Item item;
+    item.mType = TYPE_STORAGE;
+    item.mObj = NULL;
+    item.mPtr = malloc(size);
+    mItems.push_back(item);
+
+    return item.mPtr;
+}
+
+const hidl_string *EphemeralStorage::allocTemporaryString(
+        JNIEnv *env, jstring stringObj) {
+    jstring obj = (jstring)env->NewGlobalRef(stringObj);
+    const char *val = env->GetStringUTFChars(obj, NULL);
+
+    Item item;
+    item.mType = TYPE_STRING;
+    item.mObj = obj;
+    item.mPtr = (void *)val;
+    mItems.push_back(item);
+
+    hidl_string *s = allocStringArray(1 /* size */);
+    s->setToExternal((char *)val, strlen(val));
+
+    return s;
+}
+
+#define DEFINE_ALLOC_ARRAY_METHODS(Suffix,Type,NewType)                        \
+const Type *EphemeralStorage::allocTemporary ## Suffix ## Array(               \
+        JNIEnv *env, Type ## Array arrayObj) {                                 \
+    Type ## Array obj = (Type ## Array)env->NewGlobalRef(arrayObj);            \
+    const Type *val = env->Get ## NewType ## ArrayElements(obj, NULL);         \
+                                                                               \
+    Item item;                                                                 \
+    item.mType = TYPE_ ## Suffix ## _ARRAY;                                    \
+    item.mObj = obj;                                                           \
+    item.mPtr = (void *)val;                                                   \
+    mItems.push_back(item);                                                    \
+                                                                               \
+    return val;                                                                \
+}
+
+#define DEFINE_ALLOC_VECTOR_METHODS(Suffix,Type,NewType)                       \
+const hidl_vec<Type> *EphemeralStorage::allocTemporary ## Suffix ## Vector(    \
+        JNIEnv *env, Type ## Array arrayObj) {                                 \
+    Type ## Array obj = (Type ## Array)env->NewGlobalRef(arrayObj);            \
+    jsize len = env->GetArrayLength(obj);                                      \
+    const Type *val = env->Get ## NewType ## ArrayElements(obj, NULL);         \
+                                                                               \
+    Item item;                                                                 \
+    item.mType = TYPE_ ## Suffix ## _ARRAY;                                    \
+    item.mObj = obj;                                                           \
+    item.mPtr = (void *)val;                                                   \
+    mItems.push_back(item);                                                    \
+                                                                               \
+    void *vecPtr = allocTemporaryStorage(sizeof(hidl_vec<Type>));              \
+                                                                               \
+    hidl_vec<Type> *vec = new (vecPtr) hidl_vec<Type>;                         \
+    vec->setToExternal(const_cast<Type *>(val), len);                          \
+                                                                               \
+    return vec;                                                                \
+}
+
+DEFINE_ALLOC_ARRAY_METHODS(Int8,jbyte,Byte)
+DEFINE_ALLOC_ARRAY_METHODS(Int16,jshort,Short)
+DEFINE_ALLOC_ARRAY_METHODS(Int32,jint,Int)
+DEFINE_ALLOC_ARRAY_METHODS(Int64,jlong,Long)
+DEFINE_ALLOC_ARRAY_METHODS(Float,jfloat,Float)
+DEFINE_ALLOC_ARRAY_METHODS(Double,jdouble,Double)
+
+DEFINE_ALLOC_VECTOR_METHODS(Int8,jbyte,Byte)
+DEFINE_ALLOC_VECTOR_METHODS(Int16,jshort,Short)
+DEFINE_ALLOC_VECTOR_METHODS(Int32,jint,Int)
+DEFINE_ALLOC_VECTOR_METHODS(Int64,jlong,Long)
+DEFINE_ALLOC_VECTOR_METHODS(Float,jfloat,Float)
+DEFINE_ALLOC_VECTOR_METHODS(Double,jdouble,Double)
+
+#define DEFINE_RELEASE_ARRAY_CASE(Suffix,Type,NewType)                         \
+            case TYPE_ ## Suffix ## _ARRAY:                                    \
+            {                                                                  \
+                env->Release ## NewType ## ArrayElements(                      \
+                        (Type ## Array)item.mObj,                              \
+                        (Type *)item.mPtr,                                     \
+                        0 /* mode */);                                         \
+                                                                               \
+                env->DeleteGlobalRef(item.mObj);                               \
+                break;                                                         \
+            }
+
+void EphemeralStorage::release(JNIEnv *env) {
+    for (size_t i = mItems.size(); i--;) {
+        const Item &item = mItems[i];
+
+        switch (item.mType) {
+            case TYPE_STRING_ARRAY:
+            {
+                delete[] static_cast<hidl_string *>(item.mPtr);
+                break;
+            }
+
+            case TYPE_STORAGE:
+            {
+                free(item.mPtr);
+                break;
+            }
+
+            case TYPE_STRING:
+            {
+                env->ReleaseStringUTFChars(
+                        (jstring)item.mObj, (const char *)item.mPtr);
+
+                env->DeleteGlobalRef(item.mObj);
+                break;
+            }
+
+            DEFINE_RELEASE_ARRAY_CASE(Int8,jbyte,Byte)
+            DEFINE_RELEASE_ARRAY_CASE(Int16,jshort,Short)
+            DEFINE_RELEASE_ARRAY_CASE(Int32,jint,Int)
+            DEFINE_RELEASE_ARRAY_CASE(Int64,jlong,Long)
+            DEFINE_RELEASE_ARRAY_CASE(Float,jfloat,Float)
+            DEFINE_RELEASE_ARRAY_CASE(Double,jdouble,Double)
+
+            default:
+                CHECK(!"Should not be here");
+        }
+    }
+
+    mItems.clear();
+}
+
+}  // namespace android
+
diff --git a/core/jni/hwbinder/EphemeralStorage.h b/core/jni/hwbinder/EphemeralStorage.h
new file mode 100644
index 0000000..b02ed9f
--- /dev/null
+++ b/core/jni/hwbinder/EphemeralStorage.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#ifndef EPHEMERAL_STORAGE_H_
+
+#define EPHEMERAL_STORAGE_H_
+
+#include <android-base/macros.h>
+#include <hidl/HidlSupport.h>
+#include <jni.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+#define DECLARE_ALLOC_METHODS(Suffix,Type)                          \
+    const Type *allocTemporary ## Suffix ## Array(                  \
+            JNIEnv *env, Type ## Array arrayObj);                   \
+                                                                    \
+    const ::android::hardware::hidl_vec<Type> *                     \
+    allocTemporary ## Suffix ## Vector(                             \
+            JNIEnv *env, Type ## Array arrayObj);
+
+struct EphemeralStorage {
+    EphemeralStorage();
+    ~EphemeralStorage();
+
+    void release(JNIEnv *env);
+
+    hardware::hidl_string *allocStringArray(size_t size);
+
+    void *allocTemporaryStorage(size_t size);
+
+    const ::android::hardware::hidl_string *allocTemporaryString(
+            JNIEnv *env, jstring stringObj);
+
+    DECLARE_ALLOC_METHODS(Int8,jbyte)
+    DECLARE_ALLOC_METHODS(Int16,jshort)
+    DECLARE_ALLOC_METHODS(Int32,jint)
+    DECLARE_ALLOC_METHODS(Int64,jlong)
+    DECLARE_ALLOC_METHODS(Float,jfloat)
+    DECLARE_ALLOC_METHODS(Double,jdouble)
+
+private:
+    enum Type {
+        TYPE_STRING_ARRAY,
+        TYPE_STORAGE,
+        TYPE_STRING,
+        TYPE_Int8_ARRAY,
+        TYPE_Int16_ARRAY,
+        TYPE_Int32_ARRAY,
+        TYPE_Int64_ARRAY,
+        TYPE_Float_ARRAY,
+        TYPE_Double_ARRAY,
+    };
+
+    struct Item {
+        Type mType;
+        jobject mObj;
+        void *mPtr;
+    };
+
+    Vector<Item> mItems;
+
+    DISALLOW_COPY_AND_ASSIGN(EphemeralStorage);
+};
+
+#undef DECLARE_ALLOC_METHODS
+
+}  // namespace android
+
+#endif  // EPHEMERAL_STORAGE_H_
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk
new file mode 100644
index 0000000..a6f87ea
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := 18
+
+LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp_corrupted
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+corrupted_classes2_dex := $(dir $(built_dex))/classes2.dex
+
+$(corrupted_classes2_dex): $(built_dex)
+	$(hide) touch $@
+
+$(LOCAL_BUILT_MODULE): $(corrupted_classes2_dex)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
new file mode 100644
index 0000000..7993c6f
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.framework.multidexlegacycorrupteddex"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk
+        android:minSdkVersion="18"
+        android:targetSdkVersion="18" />
+
+    <application
+        android:name="android.support.multidex.MultiDexApplication"
+        android:allowBackup="true"
+        android:label="MultiDexLegacyTestApp_corrupted">
+        <activity
+            android:name="com.android.framework.multidexlegacycorrupteddex.MainActivity"
+            android:label="MultiDexLegacyTestApp_corrupted" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="com.android.framework.multidexlegacycorrupteddex"
+                     android:label="Test for MultiDexLegacyTestApp_corrupted" />
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/res/layout/activity_main.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/res/layout/activity_main.xml
new file mode 100644
index 0000000..58ae67a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/res/layout/activity_main.xml
@@ -0,0 +1,7 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity" >
+
+</RelativeLayout>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/CorruptedDexTest.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/CorruptedDexTest.java
new file mode 100644
index 0000000..afef394
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/CorruptedDexTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacycorrupteddex;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+/**
+ * Run the tests with: <code>adb shell am instrument -w
+ com.android.framework.multidexlegacycorrupteddex/android.test.InstrumentationTestRunner
+</code>
+ */
+public class CorruptedDexTest extends ActivityInstrumentationTestCase2<MainActivity>
+{
+
+    public CorruptedDexTest() {
+        super(MainActivity.class);
+    }
+
+    /**
+     * Tests that when a {@link ClassNotFoundException} is thrown, the message also contains
+     * something about the suppressed IOException.
+     */
+    public void testSupressedExceptions()
+    {
+        try {
+            Class.forName("notapackage.NotAClass");
+            throw new AssertionError();
+        } catch (ClassNotFoundException e) {
+            // expected
+
+//          This the check we should do but API is not yet available in 19
+//          Throwable[] suppressed = e.getSuppressed();
+//          assertTrue(suppressed.length > 0);
+//          boolean ioFound = false;
+//          for (Throwable throwable : suppressed) {
+//            if (throwable instanceof IOException) {
+//              ioFound = true;
+//              break;
+//            }
+//          }
+//          assertTrue(ioFound);
+
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            PrintStream ps = new PrintStream(baos);
+            e.printStackTrace(ps);
+            ps.close();
+            assertTrue("Exception message should mention IOException but is not: "
+                  + baos.toString(),
+              baos.toString().contains("IOException"));
+        }
+    }
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/MainActivity.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/MainActivity.java
new file mode 100644
index 0000000..61ba5b8
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/src/com/android/framework/multidexlegacycorrupteddex/MainActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 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.framework.multidexlegacycorrupteddex;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class MainActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+    }
+
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
index da8bc1d..7935880 100644
--- a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
@@ -45,6 +45,7 @@
     private static final int TEST_CMD = 18;
     private static final int TEST_ARG1 = 33;
     private static final int TEST_ARG2 = 182;
+    private static final Object TEST_OBJ = "hello";
 
     @Mock AlarmManager mAlarmManager;
     WakeupMessage mMessage;
@@ -92,7 +93,7 @@
                 mListenerCaptor.capture(), any(Handler.class));
 
         mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1,
-                TEST_ARG2);
+                TEST_ARG2, TEST_OBJ);
     }
 
     /**
@@ -114,6 +115,7 @@
         assertEquals("what", TEST_CMD, mHandler.getLastMessage().what);
         assertEquals("arg1", TEST_ARG1, mHandler.getLastMessage().arg1);
         assertEquals("arg2", TEST_ARG2, mHandler.getLastMessage().arg2);
+        assertEquals("obj", TEST_OBJ, mHandler.getLastMessage().obj);
     }
 
     /**
diff --git a/docs/html/guide/topics/renderscript/compute.jd b/docs/html/guide/topics/renderscript/compute.jd
index c5b49d7..b30c741 100755
--- a/docs/html/guide/topics/renderscript/compute.jd
+++ b/docs/html/guide/topics/renderscript/compute.jd
@@ -10,12 +10,13 @@
 
     <ol>
       <li><a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a></li>
-      <li><a href="#access-rs-apis">Accessing RenderScript APIs</a>
+      <li><a href="#access-rs-apis">Accessing RenderScript APIs from Java</a>
         <ol>
           <li><a href="#ide-setup">Setting Up Your Development Environment</a></li>
         </ol>
       </li>
       <li><a href="#using-rs-from-java">Using RenderScript from Java Code</a></li>
+      <li><a href="#single-source-rs">Single-Source RenderScript</a></li>
       <li><a href="#reduction-in-depth">Reduction Kernels in Depth</a>
         <ol>
           <li><a href="#writing-reduction-kernel">Writing a reduction kernel</a></li>
@@ -45,12 +46,16 @@
 <p>To begin with RenderScript, there are two main concepts you should understand:</p>
 <ul>
 
-<li>High-performance compute kernels are written in a C99-derived language. A <i>compute
-    kernel</i> is a function or collection of functions that you can direct the RenderScript runtime
-    to execute in parallel across a collection of data.</li>
+<li>The <em>language</em> itself is a C99-derived language for writing high-performance compute
+code. <a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> describes
+how to use it to write compute kernels.</li>
 
-<li>A Java API is used for managing the lifetime of RenderScript resources and controlling kernel
-execution.</li>
+<li>The <em>control API</em> is used for managing the lifetime of RenderScript resources and
+controlling kernel execution. It is available in three different languages: Java, C++ in Android
+NDK, and the C99-derived kernel language itself.
+<a href="#using-rs-from-java">Using RenderScript from Java Code</a> and
+<a href=#single-source-rs>Single-Source RenderScript</a> describe the first and the third
+options, respectively.</li>
 </ul>
 
 <h2 id="writing-an-rs-kernel">Writing a RenderScript Kernel</h2>
@@ -77,7 +82,9 @@
 access script globals from Java code, and these are often used for parameter passing to RenderScript
 kernels.</p></li>
 
-<li><p>Zero or more <strong><i>compute kernels</i></strong>. There are two kinds of compute
+<li><p>Zero or more <strong><i>compute kernels</i></strong>. A compute kernel is a function
+or collection of functions that you can direct the RenderScript runtime to execute in parallel
+across a collection of data. There are two kinds of compute
 kernels: <i>mapping</i> kernels (also called <i>foreach</i> kernels)
 and <i>reduction</i> kernels.</p>
 
@@ -243,9 +250,9 @@
 precision (such as SIMD CPU instructions).</p>
 
 
-<h2 id="access-rs-apis">Accessing RenderScript APIs</h2>
+<h2 id="access-rs-apis">Accessing RenderScript APIs from Java</h2>
 
-<p>When developing an Android application that uses RenderScript, you can access its API in
+<p>When developing an Android application that uses RenderScript, you can access its API from Java in
   one of two ways:</p>
 
 <ul>
@@ -377,7 +384,7 @@
 <ul>
 
 <li><strong>ScriptC</strong>: These are the user-defined scripts as described in <a
-href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> above. Every script has a Java class
+href="#writing-an-rs-kernel"><i>Writing a RenderScript Kernel</i></a> above. Every script has a Java class
 reflected by the RenderScript compiler in order to make it easy to access the script from Java code;
 this class has the name <code>ScriptC_<i>filename</i></code>. For example, if the mapping kernel
 above were located in <code>invert.rs</code> and a RenderScript context were already located in
@@ -448,6 +455,116 @@
   a <code>get()</code> method to obtain the result of a reduction. <code>get()</code> is
   synchronous, and is serialized with respect to the reduction (which is asynchronous).</p>
 
+<h2 id="single-source-rs">Single-Source RenderScript</h2>
+
+<p>Android 7.0 (API level 24) introduces a new programming feature called <em>Single-Source
+RenderScript</em>, in which kernels are launched from the script where they are defined, rather than
+from Java. This approach is currently limited to mapping kernels, which are simply referred to as "kernels"
+in this section for conciseness. This new feature also supports creating allocations of type
+<a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_allocation>
+<code>rs_allocation</code></a> from inside the script. It is now possible to
+implement a whole algorithm solely within a script, even if multiple kernel launches are required.
+The benefit is twofold: more readable code, because it keeps the implementation of an algorithm in
+one language; and potentially faster code, because of fewer transitions between Java and
+RenderScript across multiple kernel launches.</p>
+
+<p>In Single-Source RenderScript, you write kernels as described in <a href="#writing-an-rs-kernel">
+Writing a RenderScript Kernel</a>. You then write an invokable function that calls
+<a href="{@docRoot}guide/topics/renderscript/reference/rs_for_each.html#android_rs:rsForEach">
+<code>rsForEach()</code></a> to launch them. That API takes a kernel function as the first
+parameter, followed by input and output allocations. A similar API
+<a href="{@docRoot}guide/topics/renderscript/reference/rs_for_each.html#android_rs:rsForEachWithOptions">
+<code>rsForEachWithOptions()</code></a> takes an extra argument of type
+<a href="{@docRoot}guide/topics/renderscript/reference/rs_for_each.html#android_rs:rs_script_call_t">
+<code>rs_script_call_t</code></a>, which specifies a subset of the elements from the input and
+output allocations for the kernel function to process.</p>
+
+<p>To start RenderScript computation, you call the invokable function from Java.
+Follow the steps in <a href="#using-rs-from-java">Using RenderScript from Java Code</a>.
+In the step <a href="#launching_kernels">launch the appropriate kernels</a>, call
+the invokable function using <code>invoke_<i>function_name</i>()</code>, which will start the
+whole computation, including launching kernels.</p>
+
+<p>Allocations are often needed to save and pass
+intermediate results from one kernel launch to another. You can create them using
+<a href="{@docRoot}guide/topics/renderscript/reference/rs_allocation_create.html#android_rs:rsCreateAllocation">
+rsCreateAllocation()</a>. One easy-to-use form of that API is <code>
+rsCreateAllocation_&ltT&gt&ltW&gt(&hellip;)</code>, where <i>T</i> is the data type for an
+element, and <i>W</i> is the vector width for the element. The API takes the sizes in
+dimensions X, Y, and Z as arguments. For 1D or 2D allocations, the size for dimension Y or Z can
+be omitted. For example, <code>rsCreateAllocation_uchar4(16384)</code> creates a 1D allocation of
+16384 elements, each of which is of type <code>uchar4</code>.</p>
+
+<p>Allocations are managed by the system automatically. You
+do not have to explicitly release or free them. However, you can call
+<a href="{@docRoot}guide/topics/renderscript/reference/rs_object_info.html#android_rs:rsClearObject">
+<code>rsClearObject(rs_allocation* alloc)</code></a> to indicate you no longer need the handle
+<code>alloc</code> to the underlying allocation,
+so that the system can free up resources as early as possible.</p>
+
+<p>The <a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> section contains an example
+kernel that inverts an image. The example below expands that to apply more than one effect to an image,
+using Single-Source RenderScript. It includes another kernel, <code>greyscale</code>, which turns a
+color image into black-and-white. An invokable function <code>process()</code> then applies those two kernels
+consecutively to an input image, and produces an output image. Allocations for both the input and
+the output are passed in as arguments of type
+<a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_allocation>
+<code>rs_allocation</code></a>.</p>
+
+<pre>
+// File: singlesource.rs
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rssample)
+
+static const float4 weight = {0.299f, 0.587f, 0.114f, 0.0f};
+
+uchar4 RS_KERNEL invert(uchar4 in, uint32_t x, uint32_t y) {
+  uchar4 out = in;
+  out.r = 255 - in.r;
+  out.g = 255 - in.g;
+  out.b = 255 - in.b;
+  return out;
+}
+
+uchar4 RS_KERNEL greyscale(uchar4 in) {
+  const float4 inF = rsUnpackColor8888(in);
+  const float4 outF = (float4){ dot(inF, weight) };
+  return rsPackColorTo8888(outF);
+}
+
+void process(rs_allocation inputImage, rs_allocation outputImage) {
+  const uint32_t imageWidth = rsAllocationGetDimX(inputImage);
+  const uint32_t imageHeight = rsAllocationGetDimY(inputImage);
+  rs_allocation tmp = rsCreateAllocation_uchar4(imageWidth, imageHeight);
+  rsForEach(invert, inputImage, tmp);
+  rsForEach(greyscale, tmp, outputImage);
+}
+</pre>
+
+<p>You can call the <code>process()</code> function from Java as follows:</p>
+
+<pre>
+// File SingleSource.java
+
+RenderScript RS = RenderScript.create(context);
+ScriptC_singlesource script = new ScriptC_singlesource(RS);
+Allocation inputAllocation = Allocation.createFromBitmapResource(
+    RS, getResources(), R.drawable.image);
+Allocation outputAllocation = Allocation.createTyped(
+    RS, inputAllocation.getType(),
+    Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_OUTPUT);
+script.invoke_process(inputAllocation, outputAllocation);
+</pre>
+
+<p>This example shows how an algorithm that involves two kernel launches can be implemented completely
+in the RenderScript language itself. Without Single-Source
+RenderScript, you would have to launch both kernels from the Java code, separating kernel launches
+from kernel definitions and making it harder to understand the whole algorithm. Not only is the
+Single-Source RenderScript code easier to read, it also eliminates the transitioning
+between Java and the script across kernel launches. Some iterative algorithms may launch kernels
+hundreds of times, making the overhead of such transitioning considerable.</p>
+
 <h2 id="reduction-in-depth">Reduction Kernels in Depth</h2>
 
 <p><i>Reduction</i> is the process of combining a collection of data into a single
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index 6eb2eef..32ab501 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -97,9 +97,9 @@
 };
 
 struct ClipBase {
-    ClipBase(ClipMode mode)
+    explicit ClipBase(ClipMode mode)
             : mode(mode) {}
-    ClipBase(const Rect& rect)
+    explicit ClipBase(const Rect& rect)
             : mode(ClipMode::Rectangle)
             , rect(rect) {}
     const ClipMode mode;
@@ -112,19 +112,19 @@
 };
 
 struct ClipRect : ClipBase {
-    ClipRect(const Rect& rect)
+    explicit ClipRect(const Rect& rect)
             : ClipBase(rect) {}
 };
 
 struct ClipRectList : ClipBase {
-    ClipRectList(const RectangleList& rectList)
+    explicit ClipRectList(const RectangleList& rectList)
             : ClipBase(ClipMode::RectangleList)
             , rectList(rectList) {}
     RectangleList rectList;
 };
 
 struct ClipRegion : ClipBase {
-    ClipRegion(const SkRegion& region)
+    explicit ClipRegion(const SkRegion& region)
             : ClipBase(ClipMode::Region)
             , region(region) {}
     ClipRegion()
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 44a24c8..4005822 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -35,7 +35,7 @@
 public:
     // Note that DeferredLayerUpdater assumes it is taking ownership of the layer
     // and will not call incrementRef on it as a result.
-    ANDROID_API DeferredLayerUpdater(Layer* layer);
+    ANDROID_API explicit DeferredLayerUpdater(Layer* layer);
     ANDROID_API ~DeferredLayerUpdater();
 
     ANDROID_API bool setSize(int width, int height) {
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index e10a81b..d656864 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -96,7 +96,7 @@
 
 class FontRenderer {
 public:
-    FontRenderer(const uint8_t* gammaTable);
+    explicit FontRenderer(const uint8_t* gammaTable);
     ~FontRenderer();
 
     void flushLargeCaches(std::vector<CacheTexture*>& cacheTextures);
diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h
index 851aeae..bfb1bf1 100644
--- a/libs/hwui/GpuMemoryTracker.h
+++ b/libs/hwui/GpuMemoryTracker.h
@@ -52,7 +52,7 @@
     static void onFrameCompleted();
 
 protected:
-    GpuMemoryTracker(GpuObjectType type) : mType(type) {
+    explicit GpuMemoryTracker(GpuObjectType type) : mType(type) {
         ASSERT_GPU_THREAD();
         startTrackingObject();
     }
diff --git a/libs/hwui/PropertyValuesAnimatorSet.h b/libs/hwui/PropertyValuesAnimatorSet.h
index c7ae7c0..602fd91 100644
--- a/libs/hwui/PropertyValuesAnimatorSet.h
+++ b/libs/hwui/PropertyValuesAnimatorSet.h
@@ -73,7 +73,7 @@
 
 class PropertyAnimatorSetListener : public AnimationListener {
 public:
-    PropertyAnimatorSetListener(PropertyValuesAnimatorSet* set) : mSet(set) {}
+    explicit PropertyAnimatorSetListener(PropertyValuesAnimatorSet* set) : mSet(set) {}
     virtual void onAnimationFinished(BaseRenderNodeAnimator* animator) override;
 
 private:
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index aee9d63..5497f86 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -509,7 +509,7 @@
             , mode(PaintUtils::getXfermodeDirect(paint))
             , colorFilter(paint ? paint->getColorFilter() : nullptr) {}
 
-    LayerOp(RenderNode& node)
+    explicit LayerOp(RenderNode& node)
             : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), nullptr, nullptr)
             , layerHandle(node.getLayerHandle())
             , alpha(node.properties().layerProperties().alpha() / 255.0f)
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index a5d1d4b..3586d8a 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -90,7 +90,7 @@
 public:
     class Properties {
     public:
-        Properties(Node* node) : mNode(node) {}
+        explicit Properties(Node* node) : mNode(node) {}
         inline void onPropertyChanged() {
             mNode->onPropertyChanged(this);
         }
@@ -132,7 +132,7 @@
 
     class PathProperties : public Properties {
     public:
-        PathProperties(Node* node) : Properties(node) {}
+        explicit PathProperties(Node* node) : Properties(node) {}
         void syncProperties(const PathProperties& prop) {
             mData = prop.mData;
             onPropertyChanged();
@@ -218,7 +218,7 @@
             float strokeMiterLimit = 4;
             int fillType = 0; /* non-zero or kWinding_FillType in Skia */
         };
-        FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {}
+        explicit FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {}
         ~FullPathProperties() {
             SkSafeUnref(fillGradient);
             SkSafeUnref(strokeGradient);
@@ -409,7 +409,7 @@
 public:
     class GroupProperties : public Properties {
     public:
-        GroupProperties(Node* mNode) : Properties(mNode) {}
+        explicit GroupProperties(Node* mNode) : Properties(mNode) {}
         struct PrimitiveFields {
             float rotate = 0;
             float pivotX = 0;
@@ -539,7 +539,7 @@
 
 class ANDROID_API Tree : public VirtualLightRefBase {
 public:
-    Tree(Group* rootNode) : mRootNode(rootNode) {
+    explicit Tree(Group* rootNode) : mRootNode(rootNode) {
         mRootNode->setPropertyChangedListener(&mPropertyChangedListener);
     }
 
@@ -576,7 +576,7 @@
 
     class TreeProperties {
     public:
-        TreeProperties(Tree* tree) : mTree(tree) {}
+        explicit TreeProperties(Tree* tree) : mTree(tree) {}
         // Properties that can only be modified by UI thread, therefore sync should
         // only go from UI to RT
         struct NonAnimatableProperties {
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 9599c30..f72a632 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -30,7 +30,7 @@
 public:
     Paint();
     Paint(const Paint& paint);
-    Paint(const SkPaint& paint);
+    Paint(const SkPaint& paint);  // NOLINT(implicit)
     ~Paint();
 
     Paint& operator=(const Paint& other);
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h
index 73a3392..26d4e36 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.h
+++ b/libs/hwui/renderstate/OffscreenBufferPool.h
@@ -126,7 +126,7 @@
                 : width(OffscreenBuffer::computeIdealDimension(layerWidth))
                 , height(OffscreenBuffer::computeIdealDimension(layerHeight)) {}
 
-        Entry(OffscreenBuffer* layer)
+        explicit Entry(OffscreenBuffer* layer)
                 : layer(layer)
                 , width(layer->texture.width())
                 , height(layer->texture.height()) {
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index 706f2ff..060984e 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -55,7 +55,7 @@
 
     class Registrar {
     public:
-        Registrar(const TestScene::Info& info) {
+        explicit Registrar(const TestScene::Info& info) {
             TestScene::registerScene(info);
         }
     private:
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index a48469c..5a4ab99 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -102,7 +102,7 @@
     public:
         SignalingDtor()
                 : mSignal(nullptr) {}
-        SignalingDtor(int* signal)
+        explicit SignalingDtor(int* signal)
                 : mSignal(signal) {}
         void setSignal(int* signal) {
             mSignal = signal;
@@ -202,7 +202,7 @@
 
     class TestTask : public renderthread::RenderTask {
     public:
-        TestTask(RtCallback rtCallback)
+        explicit TestTask(RtCallback rtCallback)
                 : rtCallback(rtCallback) {}
         virtual ~TestTask() {}
         virtual void run() override;
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
index 93d37c2..df8cb076 100644
--- a/libs/hwui/utils/FatVector.h
+++ b/libs/hwui/utils/FatVector.h
@@ -53,7 +53,7 @@
     typedef T value_type; // needed to implement std::allocator
     typedef T* pointer; // needed to implement std::allocator
 
-    InlineStdAllocator(Allocation& allocation)
+    explicit InlineStdAllocator(Allocation& allocation)
             : mAllocation(allocation) {}
     InlineStdAllocator(const InlineStdAllocator& other)
             : mAllocation(other.mAllocation) {}
@@ -93,7 +93,7 @@
         this->reserve(SIZE);
     }
 
-    FatVector(size_t capacity) : FatVector() {
+    explicit FatVector(size_t capacity) : FatVector() {
         this->resize(capacity);
     }
 
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index 34c8c6b..f95a6fe 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -157,7 +157,7 @@
     typedef T value_type; // needed to implement std::allocator
     typedef T* pointer; // needed to implement std::allocator
 
-    LinearStdAllocator(LinearAllocator& allocator)
+    explicit LinearStdAllocator(LinearAllocator& allocator)
             : linearAllocator(allocator) {}
     LinearStdAllocator(const LinearStdAllocator& other)
             : linearAllocator(other.linearAllocator) {}
@@ -170,7 +170,7 @@
     };
     // enable allocators to be constructed from other templated types
     template <class U>
-    LinearStdAllocator(const LinearStdAllocator<U>& other)
+    LinearStdAllocator(const LinearStdAllocator<U>& other)  // NOLINT(implicit)
             : linearAllocator(other.linearAllocator) {}
 
     T* allocate(size_t num, const void* = 0) {
@@ -195,7 +195,7 @@
 template <class T>
 class LsaVector : public std::vector<T, LinearStdAllocator<T>> {
 public:
-    LsaVector(const LinearStdAllocator<T>& allocator)
+    explicit LsaVector(const LinearStdAllocator<T>& allocator)
             : std::vector<T, LinearStdAllocator<T>>(allocator) {}
 };
 
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 905833c..7fc8d6f 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -214,7 +214,7 @@
         virtual ~SpriteImpl();
 
     public:
-        SpriteImpl(const sp<SpriteController> controller);
+        explicit SpriteImpl(const sp<SpriteController> controller);
 
         virtual void setIcon(const SpriteIcon& icon);
         virtual void setVisible(bool visible);
diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h
index 8184f94..39c1554 100644
--- a/media/jni/android_media_Utils.h
+++ b/media/jni/android_media_Utils.h
@@ -37,7 +37,7 @@
     size_t mPosition;
 
 public:
-    AssetStream(SkStream* stream);
+    explicit AssetStream(SkStream* stream);
     ~AssetStream();
 
     // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
@@ -60,7 +60,7 @@
     const size_t kMinSizeToRead = 8192;
 
 public:
-    BufferedStream(SkStream* stream);
+    explicit BufferedStream(SkStream* stream);
     ~BufferedStream();
 
     // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
@@ -79,8 +79,8 @@
     size_t mPosition;
 
 public:
-    FileStream(const int fd);
-    FileStream(const String8 filename);
+    explicit FileStream(const int fd);
+    explicit FileStream(const String8 filename);
     ~FileStream();
 
     // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
diff --git a/media/mca/filterfw/native/core/gl_frame.h b/media/mca/filterfw/native/core/gl_frame.h
index f2a1ad5..fdbb1f5 100644
--- a/media/mca/filterfw/native/core/gl_frame.h
+++ b/media/mca/filterfw/native/core/gl_frame.h
@@ -38,7 +38,7 @@
     // Create an empty GL frame in the specified GL environment. Note, that the GLFrame does NOT
     // take ownership. The caller must make sure the GLEnv stays valid as long as the GLFrame is
     // alive.
-    GLFrame(GLEnv* gl_env);
+    explicit GLFrame(GLEnv* gl_env);
 
     // Deallocate a GL frame.
     ~GLFrame();
diff --git a/media/mca/filterfw/native/core/native_frame.h b/media/mca/filterfw/native/core/native_frame.h
index 0d335b3..2da557d 100644
--- a/media/mca/filterfw/native/core/native_frame.h
+++ b/media/mca/filterfw/native/core/native_frame.h
@@ -27,7 +27,7 @@
 class NativeFrame {
   public:
     // Create an empty native frame.
-    NativeFrame(int size);
+    explicit NativeFrame(int size);
 
     ~NativeFrame();
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 39a3412..9df5690 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -16,11 +16,13 @@
 
 package com.android.systemui;
 
+import android.app.ActivityThread;
 import android.app.Application;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.os.Process;
 import android.os.SystemProperties;
@@ -105,6 +107,13 @@
                 }
             }, filter);
         } else {
+            // We don't need to startServices for sub-process that is doing some tasks.
+            // (screenshots, sweetsweetdesserts or tuner ..)
+            String processName = ActivityThread.currentProcessName();
+            ApplicationInfo info = getApplicationInfo();
+            if (processName != null && processName.startsWith(info.processName + ":")) {
+                return;
+            }
             // For a secondary user, boot-completed will never be called because it has already
             // been broadcasted on startup for the primary SystemUI process.  Instead, for
             // components which require the SystemUI component to be initialized per-user, we
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index cdd977b..2cf93ab 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -922,7 +922,7 @@
 
         // Record user as started so newly mounted volumes kick off events
         // correctly, then synthesize events for any already-mounted volumes.
-        synchronized (mVolumes) {
+        synchronized (mLock) {
             for (int i = 0; i < mVolumes.size(); i++) {
                 final VolumeInfo vol = mVolumes.valueAt(i);
                 if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) {
@@ -945,7 +945,7 @@
         } catch (NativeDaemonConnectorException ignored) {
         }
 
-        synchronized (mVolumes) {
+        synchronized (mLock) {
             mSystemUnlockedUsers = ArrayUtils.removeInt(mSystemUnlockedUsers, userId);
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 08b204a..867168a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6703,7 +6703,7 @@
 
         ArraySet<String> completedIsas = new ArraySet<String>();
         for (String abi : Build.SUPPORTED_ABIS) {
-            Process.establishZygoteConnectionForAbi(abi);
+            Process.zygoteProcess.establishZygoteConnectionForAbi(abi);
             final String instructionSet = VMRuntime.getInstructionSet(abi);
             if (!completedIsas.contains(instructionSet)) {
                 try {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 330f46a..5ae408b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -163,7 +163,6 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
@@ -366,7 +365,6 @@
 
     private final AppOpsManager mAppOps;
 
-    private final MyPackageMonitor mPackageMonitor;
     private final IPackageManager mIPm;
 
 
@@ -411,8 +409,6 @@
 
         mAppOps = context.getSystemService(AppOpsManager.class);
 
-        mPackageMonitor = new MyPackageMonitor();
-
         // Expose private service for system components to use.
         LocalServices.addService(NetworkPolicyManagerInternal.class,
                 new NetworkPolicyManagerInternalImpl());
@@ -540,8 +536,6 @@
 
         mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
 
-        mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
-
         synchronized (mRulesLock) {
             updatePowerSaveWhitelistLocked();
             mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
@@ -731,6 +725,7 @@
             if (LOGV) Slog.v(TAG, "ACTION_UID_REMOVED for uid=" + uid);
             synchronized (mRulesLock) {
                 mUidPolicy.delete(uid);
+                removeRestrictBackgroundWhitelistedUidLocked(uid, true, true);
                 updateRestrictionRulesForUidLocked(uid);
                 writePolicyLocked();
             }
@@ -3479,18 +3474,6 @@
         }
     }
 
-    private class MyPackageMonitor extends PackageMonitor {
-
-        @Override
-        public void onPackageRemoved(String packageName, int uid) {
-            if (LOGV) Slog.v(TAG, "onPackageRemoved: " + packageName + " ->" + uid);
-            synchronized (mRulesLock) {
-                removeRestrictBackgroundWhitelistedUidLocked(uid, true, true);
-                updateRestrictionRulesForUidLocked(uid);
-            }
-        }
-    }
-
     private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal {
 
         @Override
diff --git a/services/core/java/com/android/server/pm/AbstractStatsBase.java b/services/core/java/com/android/server/pm/AbstractStatsBase.java
new file mode 100644
index 0000000..612c476
--- /dev/null
+++ b/services/core/java/com/android/server/pm/AbstractStatsBase.java
@@ -0,0 +1,126 @@
+/*
+ * 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.pm;
+
+import android.os.Environment;
+import android.os.SystemClock;
+import android.util.AtomicFile;
+
+import java.io.File;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A simple base class for statistics that need to be saved/restored from a dedicated file. This
+ * class provides a base implementation that:
+ * <ul>
+ * <li>Provide an AtomicFile to the actual read/write code
+ * <li>A background-thread write and a synchronous write
+ * <li>Write-limiting for the background-thread (by default writes are at least 30 minutes apart)
+ * <li>Can lock on the provided data object before writing
+ * </ul>
+ * For completion, a subclass needs to implement actual {@link #writeInternal(Object) writing} and
+ * {@link #readInternal(Object) reading}.
+ */
+public abstract class AbstractStatsBase<T> {
+
+    private static final int WRITE_INTERVAL_MS =
+            (PackageManagerService.DEBUG_DEXOPT) ? 0 : 30*60*1000;
+    private final Object mFileLock = new Object();
+    private final AtomicLong mLastTimeWritten = new AtomicLong(0);
+    private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false);
+    private final String mFileName;
+    private final String mBackgroundThreadName;
+    private final boolean mLock;
+
+    protected AbstractStatsBase(String fileName, String threadName, boolean lock) {
+        mFileName = fileName;
+        mBackgroundThreadName = threadName;
+        mLock = lock;
+    }
+
+    protected AtomicFile getFile() {
+        File dataDir = Environment.getDataDirectory();
+        File systemDir = new File(dataDir, "system");
+        File fname = new File(systemDir, mFileName);
+        return new AtomicFile(fname);
+    }
+
+    void writeNow(final T data) {
+        writeImpl(data);
+        mLastTimeWritten.set(SystemClock.elapsedRealtime());
+    }
+
+    boolean maybeWriteAsync(final T data) {
+        if (SystemClock.elapsedRealtime() - mLastTimeWritten.get() < WRITE_INTERVAL_MS
+            && !PackageManagerService.DEBUG_DEXOPT) {
+            return false;
+        }
+
+        if (mBackgroundWriteRunning.compareAndSet(false, true)) {
+            new Thread(mBackgroundThreadName) {
+                @Override
+                public void run() {
+                    try {
+                        writeImpl(data);
+                        mLastTimeWritten.set(SystemClock.elapsedRealtime());
+                    } finally {
+                        mBackgroundWriteRunning.set(false);
+                    }
+                }
+            }.start();
+            return true;
+        }
+
+        return false;
+    }
+
+    private void writeImpl(T data) {
+        if (mLock) {
+            synchronized (data) {
+                synchronized (mFileLock) {
+                    writeInternal(data);
+                }
+            }
+        } else {
+            synchronized (mFileLock) {
+                writeInternal(data);
+            }
+        }
+    }
+
+    protected abstract void writeInternal(T data);
+
+    void read(T data) {
+        if (mLock) {
+            synchronized (data) {
+                synchronized (mFileLock) {
+                    readInternal(data);
+                }
+            }
+        } else {
+            synchronized (mFileLock) {
+                readInternal(data);
+            }
+        }
+        // We use the current time as last-written. read() is called on system server startup
+        // (current situation), and we want to postpone I/O at boot.
+        mLastTimeWritten.set(SystemClock.elapsedRealtime());
+    }
+
+    protected abstract void readInternal(T data);
+}
diff --git a/services/core/java/com/android/server/pm/CompilerStats.java b/services/core/java/com/android/server/pm/CompilerStats.java
new file mode 100644
index 0000000..46efd98
--- /dev/null
+++ b/services/core/java/com/android/server/pm/CompilerStats.java
@@ -0,0 +1,294 @@
+/*
+ * 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.pm;
+
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Log;
+
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.IndentingPrintWriter;
+
+import libcore.io.IoUtils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A class that collects, serializes and deserializes compiler-related statistics on a
+ * per-package per-code-path basis.
+ *
+ * Currently used to track compile times.
+ */
+class CompilerStats extends AbstractStatsBase<Void> {
+
+    private final static String COMPILER_STATS_VERSION_HEADER = "PACKAGE_MANAGER__COMPILER_STATS__";
+    private final static int COMPILER_STATS_VERSION = 1;
+
+    /**
+     * Class to collect all stats pertaining to one package.
+     */
+    static class PackageStats {
+
+        private final String packageName;
+
+        /**
+         * This map stores compile-times for all code paths in the package. The value
+         * is in milliseconds.
+         */
+        private final Map<String, Long> compileTimePerCodePath;
+
+        /**
+         * @param packageName
+         */
+        public PackageStats(String packageName) {
+            this.packageName = packageName;
+            // We expect at least one element in here, but let's make it minimal.
+            compileTimePerCodePath = new ArrayMap<>(2);
+        }
+
+        public String getPackageName() {
+            return packageName;
+        }
+
+        /**
+         * Return the recorded compile time for a given code path. Returns
+         * 0 if there is no recorded time.
+         */
+        public long getCompileTime(String codePath) {
+            String storagePath = getStoredPathFromCodePath(codePath);
+            synchronized (compileTimePerCodePath) {
+                Long l = compileTimePerCodePath.get(storagePath);
+                if (l == null) {
+                    return 0;
+                }
+                return l;
+            }
+        }
+
+        public void setCompileTime(String codePath, long compileTimeInMs) {
+            String storagePath = getStoredPathFromCodePath(codePath);
+            synchronized (compileTimePerCodePath) {
+                if (compileTimeInMs <= 0) {
+                    compileTimePerCodePath.remove(storagePath);
+                } else {
+                    compileTimePerCodePath.put(storagePath, compileTimeInMs);
+                }
+            }
+        }
+
+        private static String getStoredPathFromCodePath(String codePath) {
+            int lastSlash = codePath.lastIndexOf(File.separatorChar);
+            return codePath.substring(lastSlash + 1);
+        }
+
+        public void dump(IndentingPrintWriter ipw) {
+            synchronized (compileTimePerCodePath) {
+                if (compileTimePerCodePath.size() == 0) {
+                    ipw.println("(No recorded stats)");
+                } else {
+                    for (Map.Entry<String, Long> e : compileTimePerCodePath.entrySet()) {
+                        ipw.println(" " + e.getKey() + " - " + e.getValue());
+                    }
+                }
+            }
+        }
+    }
+
+    private final Map<String, PackageStats> packageStats;
+
+    public CompilerStats() {
+        super("package-cstats.list", "CompilerStats_DiskWriter", /* lock */ false);
+        packageStats = new HashMap<>();
+    }
+
+    public PackageStats getPackageStats(String packageName) {
+        synchronized (packageStats) {
+            return packageStats.get(packageName);
+        }
+    }
+
+    public void setPackageStats(String packageName, PackageStats stats) {
+        synchronized (packageStats) {
+            packageStats.put(packageName, stats);
+        }
+    }
+
+    public PackageStats createPackageStats(String packageName) {
+        synchronized (packageStats) {
+            PackageStats newStats = new PackageStats(packageName);
+            packageStats.put(packageName, newStats);
+            return newStats;
+        }
+    }
+
+    public PackageStats getOrCreatePackageStats(String packageName) {
+        synchronized (packageStats) {
+            PackageStats existingStats = packageStats.get(packageName);
+            if (existingStats != null) {
+                return existingStats;
+            }
+
+            return createPackageStats(packageName);
+        }
+    }
+
+    public void deletePackageStats(String packageName) {
+        synchronized (packageStats) {
+            packageStats.remove(packageName);
+        }
+    }
+
+    // I/O
+
+    // The encoding is simple:
+    //
+    // 1) The first line is a line consisting of the version header and the version number.
+    //
+    // 2) The rest of the file is package data.
+    // 2.1) A package is started by any line not starting with "-";
+    // 2.2) Any line starting with "-" is code path data. The format is:
+    //      '-'{code-path}':'{compile-time}
+
+    public void write(Writer out) {
+        @SuppressWarnings("resource")
+        FastPrintWriter fpw = new FastPrintWriter(out);
+
+        fpw.print(COMPILER_STATS_VERSION_HEADER);
+        fpw.println(COMPILER_STATS_VERSION);
+
+        synchronized (packageStats) {
+            for (PackageStats pkg : packageStats.values()) {
+                synchronized (pkg.compileTimePerCodePath) {
+                    if (!pkg.compileTimePerCodePath.isEmpty()) {
+                        fpw.println(pkg.getPackageName());
+
+                        for (Map.Entry<String, Long> e : pkg.compileTimePerCodePath.entrySet()) {
+                            fpw.println("-" + e.getKey() + ":" + e.getValue());
+                        }
+                    }
+                }
+            }
+        }
+
+        fpw.flush();
+    }
+
+    public boolean read(Reader r) {
+        synchronized (packageStats) {
+            // TODO: Could make this a final switch, then we wouldn't have to synchronize over
+            //       the whole reading.
+            packageStats.clear();
+
+            try {
+                BufferedReader in = new BufferedReader(r);
+
+                // Read header, do version check.
+                String versionLine = in.readLine();
+                if (versionLine == null) {
+                    throw new IllegalArgumentException("No version line found.");
+                } else {
+                    if (!versionLine.startsWith(COMPILER_STATS_VERSION_HEADER)) {
+                        throw new IllegalArgumentException("Invalid version line: " + versionLine);
+                    }
+                    int version = Integer.parseInt(
+                            versionLine.substring(COMPILER_STATS_VERSION_HEADER.length()));
+                    if (version != COMPILER_STATS_VERSION) {
+                        // TODO: Upgrade older formats? For now, just reject and regenerate.
+                        throw new IllegalArgumentException("Unexpected version: " + version);
+                    }
+                }
+
+                // For simpler code, we ignore any data lines before the first package. We
+                // collect it in a fake package.
+                PackageStats currentPackage = new PackageStats("fake package");
+
+                String s = null;
+                while ((s = in.readLine()) != null) {
+                    if (s.startsWith("-")) {
+                        int colonIndex = s.indexOf(':');
+                        if (colonIndex == -1 || colonIndex == 1) {
+                            throw new IllegalArgumentException("Could not parse data " + s);
+                        }
+                        String codePath = s.substring(1, colonIndex);
+                        long time = Long.parseLong(s.substring(colonIndex + 1));
+                        currentPackage.setCompileTime(codePath, time);
+                    } else {
+                        currentPackage = getOrCreatePackageStats(s);
+                    }
+                }
+            } catch (Exception e) {
+                Log.e(PackageManagerService.TAG, "Error parsing compiler stats", e);
+                return false;
+            }
+
+            return true;
+        }
+    }
+
+    void writeNow() {
+        writeNow(null);
+    }
+
+    boolean maybeWriteAsync() {
+        return maybeWriteAsync(null);
+    }
+
+    @Override
+    protected void writeInternal(Void data) {
+        AtomicFile file = getFile();
+        FileOutputStream f = null;
+
+        try {
+            f = file.startWrite();
+            OutputStreamWriter osw = new OutputStreamWriter(f);
+            osw.flush();
+            file.finishWrite(f);
+        } catch (IOException e) {
+            if (f != null) {
+                file.failWrite(f);
+            }
+            Log.e(PackageManagerService.TAG, "Failed to write compiler stats", e);
+        }
+    }
+
+    void read() {
+        read((Void)null);
+    }
+
+    @Override
+    protected void readInternal(Void data) {
+        AtomicFile file = getFile();
+        BufferedReader in = null;
+        try {
+            in = new BufferedReader(new InputStreamReader(file.openRead()));
+            read(in);
+        } catch (FileNotFoundException expected) {
+        } finally {
+            IoUtils.closeQuietly(in);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 7b85a4f..72c549f 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -61,6 +61,13 @@
         mInstaller = new InstallerConnection();
     }
 
+    // Package-private installer that accepts a custom InstallerConnection. Used for
+    // OtaDexoptService.
+    Installer(Context context, InstallerConnection connection) {
+        super(context);
+        mInstaller = connection;
+    }
+
     /**
      * Yell loudly if someone tries making future calls while holding a lock on
      * the given object.
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 649a27c..bff6d2d 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -32,10 +32,12 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.os.InstallerConnection;
 import com.android.internal.os.InstallerConnection.InstallerException;
 
 import java.io.File;
 import java.io.FileDescriptor;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
@@ -48,21 +50,25 @@
     private final static String TAG = "OTADexopt";
     private final static boolean DEBUG_DEXOPT = true;
 
+    // The synthetic library dependencies denoting "no checks."
+    private final static String[] NO_LIBRARIES = new String[] { "&" };
+
     private final Context mContext;
-    private final PackageDexOptimizer mPackageDexOptimizer;
     private final PackageManagerService mPackageManagerService;
 
     // TODO: Evaluate the need for WeakReferences here.
-    private List<PackageParser.Package> mDexoptPackages;
+
+    /**
+     * The list of dexopt invocations for all work.
+     */
+    private List<String> mDexoptCommands;
+
+    private int completeSize;
 
     public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
         this.mContext = context;
         this.mPackageManagerService = packageManagerService;
 
-        // Use the package manager install and install lock here for the OTA dex optimizer.
-        mPackageDexOptimizer = new OTADexoptPackageDexOptimizer(packageManagerService.mInstaller,
-                packageManagerService.mInstallLock, context);
-
         // Now it's time to check whether we need to move any A/B artifacts.
         moveAbArtifacts(packageManagerService.mInstaller);
     }
@@ -84,13 +90,44 @@
 
     @Override
     public synchronized void prepare() throws RemoteException {
-        if (mDexoptPackages != null) {
+        if (mDexoptCommands != null) {
             throw new IllegalStateException("already called prepare()");
         }
+        final List<PackageParser.Package> important;
+        final List<PackageParser.Package> others;
         synchronized (mPackageManagerService.mPackages) {
-            mDexoptPackages = PackageManagerServiceUtils.getPackagesForDexopt(
+            // Important: the packages we need to run with ab-ota compiler-reason.
+            important = PackageManagerServiceUtils.getPackagesForDexopt(
                     mPackageManagerService.mPackages.values(), mPackageManagerService);
+            // Others: we should optimize this with the (first-)boot compiler-reason.
+            others = new ArrayList<>(mPackageManagerService.mPackages.values());
+            others.removeAll(important);
+
+            // Pre-size the array list by over-allocating by a factor of 1.5.
+            mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2);
         }
+
+        for (PackageParser.Package p : important) {
+            // Make sure that core apps are optimized according to their own "reason".
+            // If the core apps are not preopted in the B OTA, and REASON_AB_OTA is not speed
+            // (by default is speed-profile) they will be interepreted/JITed. This in itself is
+            // not a problem as we will end up doing profile guided compilation. However, some
+            // core apps may be loaded by system server which doesn't JIT and we need to make
+            // sure we don't interpret-only
+            int compilationReason = p.coreApp
+                    ? PackageManagerService.REASON_CORE_APP
+                    : PackageManagerService.REASON_AB_OTA;
+            mDexoptCommands.addAll(generatePackageDexopts(p, compilationReason));
+        }
+        for (PackageParser.Package p : others) {
+            // We assume here that there are no core apps left.
+            if (p.coreApp) {
+                throw new IllegalStateException("Found a core app that's not important");
+            }
+            mDexoptCommands.addAll(
+                    generatePackageDexopts(p, PackageManagerService.REASON_FIRST_BOOT));
+        }
+        completeSize = mDexoptCommands.size();
     }
 
     @Override
@@ -98,35 +135,52 @@
         if (DEBUG_DEXOPT) {
             Log.i(TAG, "Cleaning up OTA Dexopt state.");
         }
-        mDexoptPackages = null;
+        mDexoptCommands = null;
     }
 
     @Override
     public synchronized boolean isDone() throws RemoteException {
-        if (mDexoptPackages == null) {
+        if (mDexoptCommands == null) {
             throw new IllegalStateException("done() called before prepare()");
         }
 
-        return mDexoptPackages.isEmpty();
+        return mDexoptCommands.isEmpty();
     }
 
     @Override
-    public synchronized void dexoptNextPackage() throws RemoteException {
-        if (mDexoptPackages == null) {
+    public synchronized float getProgress() throws RemoteException {
+        // Approximate the progress by the amount of already completed commands.
+        if (completeSize == 0) {
+            return 1f;
+        }
+        int commandsLeft = mDexoptCommands.size();
+        return (completeSize - commandsLeft) / ((float)completeSize);
+    }
+
+    @Override
+    public synchronized String nextDexoptCommand() throws RemoteException {
+        if (mDexoptCommands == null) {
             throw new IllegalStateException("dexoptNextPackage() called before prepare()");
         }
-        if (mDexoptPackages.isEmpty()) {
-            // Tolerate repeated calls.
-            return;
+
+        if (mDexoptCommands.isEmpty()) {
+            return "(all done)";
         }
 
-        PackageParser.Package nextPackage = mDexoptPackages.remove(0);
+        String next = mDexoptCommands.remove(0);
 
-        if (DEBUG_DEXOPT) {
-            Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt.");
+        if (IsFreeSpaceAvailable()) {
+            return next;
+        } else {
+            mDexoptCommands.clear();
+            return "(no free space)";
         }
+    }
 
-        // Check for low space.
+    /**
+     * Check for low space. Returns true if there's space left.
+     */
+    private boolean IsFreeSpaceAvailable() {
         // TODO: If apps are not installed in the internal /data partition, we should compare
         //       against that storage's free capacity.
         File dataDir = Environment.getDataDirectory();
@@ -136,19 +190,43 @@
             throw new IllegalStateException("Invalid low memory threshold");
         }
         long usableSpace = dataDir.getUsableSpace();
-        if (usableSpace < lowThreshold) {
-            Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " +
-                    usableSpace);
-            return;
+        return (usableSpace >= lowThreshold);
+    }
+
+    /**
+     * Generate all dexopt commands for the given package.
+     */
+    private synchronized List<String> generatePackageDexopts(PackageParser.Package pkg,
+            int compilationReason) {
+        // Use our custom connection that just collects the commands.
+        RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection();
+        Installer collectingInstaller = new Installer(mContext, collectingConnection);
+
+        // Use the package manager install and install lock here for the OTA dex optimizer.
+        PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
+                collectingInstaller, mPackageManagerService.mInstallLock, mContext);
+
+        String[] libraryDependencies = pkg.usesLibraryFiles;
+        if (pkg.isSystemApp()) {
+            // For system apps, we want to avoid classpaths checks.
+            libraryDependencies = NO_LIBRARIES;
         }
 
-        mPackageDexOptimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles,
+        optimizer.performDexOpt(pkg, libraryDependencies,
                 null /* ISAs */, false /* checkProfiles */,
-                getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA));
+                getCompilerFilterForReason(compilationReason),
+                null /* CompilerStats.PackageStats */);
+
+        return collectingConnection.commands;
+    }
+
+    @Override
+    public synchronized void dexoptNextPackage() throws RemoteException {
+        throw new UnsupportedOperationException();
     }
 
     private void moveAbArtifacts(Installer installer) {
-        if (mDexoptPackages != null) {
+        if (mDexoptCommands != null) {
             throw new IllegalStateException("Should not be ota-dexopting when trying to move.");
         }
 
@@ -208,4 +286,40 @@
         }
 
     }
+
+    private static class RecordingInstallerConnection extends InstallerConnection {
+        public List<String> commands = new ArrayList<String>(1);
+
+        @Override
+        public void setWarnIfHeld(Object warnIfHeld) {
+            throw new IllegalStateException("Should not reach here");
+        }
+
+        @Override
+        public synchronized String transact(String cmd) {
+            commands.add(cmd);
+            return "0";
+        }
+
+        @Override
+        public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
+            throw new IllegalStateException("Should not reach here");
+        }
+
+        @Override
+        public boolean dumpProfiles(String gid, String packageName, String codePaths)
+                throws InstallerException {
+            throw new IllegalStateException("Should not reach here");
+        }
+
+        @Override
+        public void disconnect() {
+            throw new IllegalStateException("Should not reach here");
+        }
+
+        @Override
+        public void waitForConnection() {
+            throw new IllegalStateException("Should not reach here");
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
index ea9cf17..bbd4048 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
@@ -46,6 +46,10 @@
                     return runOtaDone();
                 case "step":
                     return runOtaStep();
+                case "next":
+                    return runOtaNext();
+                case "progress":
+                    return runOtaProgress();
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -81,6 +85,18 @@
         return 0;
     }
 
+    private int runOtaNext() throws RemoteException {
+        getOutPrintWriter().println(mInterface.nextDexoptCommand());
+        return 0;
+    }
+
+    private int runOtaProgress() throws RemoteException {
+        final float progress = mInterface.getProgress();
+        final PrintWriter pw = getOutPrintWriter();
+        pw.format("%.2f", progress);
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         final PrintWriter pw = getOutPrintWriter();
@@ -94,6 +110,8 @@
         pw.println("    Replies whether the OTA is complete or not.");
         pw.println("  step");
         pw.println("    OTA dexopt the next package.");
+        pw.println("  next");
+        pw.println("    Get parameters for OTA dexopt of the next package.");
         pw.println("  cleanup");
         pw.println("    Clean up internal states. Ends an OTA session.");
     }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 26a840d..1ef4a9f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -90,7 +90,8 @@
      * synchronized on {@link #mInstallLock}.
      */
     int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
-            String[] instructionSets, boolean checkProfiles, String targetCompilationFilter) {
+            String[] instructionSets, boolean checkProfiles, String targetCompilationFilter,
+            CompilerStats.PackageStats packageStats) {
         synchronized (mInstallLock) {
             final boolean useLock = mSystemReady;
             if (useLock) {
@@ -99,7 +100,7 @@
             }
             try {
                 return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles,
-                        targetCompilationFilter);
+                        targetCompilationFilter, packageStats);
             } finally {
                 if (useLock) {
                     mDexoptWakeLock.release();
@@ -150,7 +151,8 @@
     }
 
     private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
-            String[] targetInstructionSets, boolean checkProfiles, String targetCompilerFilter) {
+            String[] targetInstructionSets, boolean checkProfiles, String targetCompilerFilter,
+            CompilerStats.PackageStats packageStats) {
         final String[] instructionSets = targetInstructionSets != null ?
                 targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
 
@@ -174,6 +176,16 @@
             isProfileGuidedFilter = false;
         }
 
+        // Disable profile guided compilation for vmSafeMode.
+        final boolean vmSafeMode = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE)
+                != 0;
+        final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE)
+                != 0;
+        if (vmSafeMode) {
+            targetCompilerFilter = getNonProfileGuidedCompilerFilter(targetCompilerFilter);
+            isProfileGuidedFilter = false;
+        }
+
         // If we're asked to take profile updates into account, check now.
         boolean newProfile = false;
         if (checkProfiles && isProfileGuidedFilter) {
@@ -185,9 +197,6 @@
             }
         }
 
-        final boolean vmSafeMode = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
-        final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-
         boolean performedDexOpt = false;
         boolean successfulDexOpt = true;
 
@@ -254,10 +263,17 @@
                         | DEXOPT_BOOTCOMPLETE);
 
                 try {
+                    long startTime = System.currentTimeMillis();
+
                     mInstaller.dexopt(path, sharedGid, pkg.packageName, dexCodeInstructionSet,
                             dexoptNeeded, oatDir, dexFlags, targetCompilerFilter, pkg.volumeUuid,
                             sharedLibrariesPath);
                     performedDexOpt = true;
+
+                    if (packageStats != null) {
+                        long endTime = System.currentTimeMillis();
+                        packageStats.setCompileTime(path, (int)(endTime - startTime));
+                    }
                 } catch (InstallerException e) {
                     Slog.w(TAG, "Failed to dexopt", e);
                     successfulDexOpt = false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0019973..76d987b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -76,8 +76,6 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.PackageParser.PARSE_IS_PRIVILEGED;
 import static android.content.pm.PackageParser.isApkFile;
-import static android.os.Process.PACKAGE_INFO_GID;
-import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDWR;
@@ -95,6 +93,7 @@
 import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getFullCompilerFilter;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getNonProfileGuidedCompilerFilter;
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
@@ -205,7 +204,6 @@
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.AtomicFile;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.ExceptionUtils;
@@ -264,7 +262,6 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
@@ -277,7 +274,6 @@
 import java.io.FileReader;
 import java.io.FilenameFilter;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.security.DigestInputStream;
@@ -304,7 +300,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * Keep track of all those APKs everywhere.
@@ -624,7 +619,7 @@
 
     final ProtectedPackages mProtectedPackages = new ProtectedPackages();
 
-    boolean mRestoredSettings;
+    boolean mFirstBoot;
 
     // System configuration read by SystemConfig.
     final int[] mGlobalGids;
@@ -1125,204 +1120,7 @@
     final @NonNull String mSharedSystemSharedLibraryPackageName;
 
     private final PackageUsage mPackageUsage = new PackageUsage();
-
-    private class PackageUsage {
-        private static final int WRITE_INTERVAL
-            = (DEBUG_DEXOPT) ? 0 : 30*60*1000; // 30m in ms
-
-        private final Object mFileLock = new Object();
-        private final AtomicLong mLastWritten = new AtomicLong(0);
-        private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false);
-
-        private boolean mIsHistoricalPackageUsageAvailable = true;
-
-        boolean isHistoricalPackageUsageAvailable() {
-            return mIsHistoricalPackageUsageAvailable;
-        }
-
-        void write(boolean force) {
-            if (force) {
-                writeInternal();
-                return;
-            }
-            if (SystemClock.elapsedRealtime() - mLastWritten.get() < WRITE_INTERVAL
-                && !DEBUG_DEXOPT) {
-                return;
-            }
-            if (mBackgroundWriteRunning.compareAndSet(false, true)) {
-                new Thread("PackageUsage_DiskWriter") {
-                    @Override
-                    public void run() {
-                        try {
-                            writeInternal();
-                        } finally {
-                            mBackgroundWriteRunning.set(false);
-                        }
-                    }
-                }.start();
-            }
-        }
-
-        private void writeInternal() {
-            synchronized (mPackages) {
-                synchronized (mFileLock) {
-                    AtomicFile file = getFile();
-                    FileOutputStream f = null;
-                    try {
-                        f = file.startWrite();
-                        BufferedOutputStream out = new BufferedOutputStream(f);
-                        FileUtils.setPermissions(file.getBaseFile().getPath(),
-                                0640, SYSTEM_UID, PACKAGE_INFO_GID);
-                        StringBuilder sb = new StringBuilder();
-
-                        sb.append(USAGE_FILE_MAGIC_VERSION_1);
-                        sb.append('\n');
-                        out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
-
-                        for (PackageParser.Package pkg : mPackages.values()) {
-                            if (pkg.getLatestPackageUseTimeInMills() == 0L) {
-                                continue;
-                            }
-                            sb.setLength(0);
-                            sb.append(pkg.packageName);
-                            for (long usageTimeInMillis : pkg.mLastPackageUsageTimeInMills) {
-                                sb.append(' ');
-                                sb.append(usageTimeInMillis);
-                            }
-                            sb.append('\n');
-                            out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
-                        }
-                        out.flush();
-                        file.finishWrite(f);
-                    } catch (IOException e) {
-                        if (f != null) {
-                            file.failWrite(f);
-                        }
-                        Log.e(TAG, "Failed to write package usage times", e);
-                    }
-                }
-            }
-            mLastWritten.set(SystemClock.elapsedRealtime());
-        }
-
-        void readLP() {
-            synchronized (mFileLock) {
-                AtomicFile file = getFile();
-                BufferedInputStream in = null;
-                try {
-                    in = new BufferedInputStream(file.openRead());
-                    StringBuffer sb = new StringBuffer();
-
-                    String firstLine = readLine(in, sb);
-                    if (firstLine == null) {
-                        // Empty file. Do nothing.
-                    } else if (USAGE_FILE_MAGIC_VERSION_1.equals(firstLine)) {
-                        readVersion1LP(in, sb);
-                    } else {
-                        readVersion0LP(in, sb, firstLine);
-                    }
-                } catch (FileNotFoundException expected) {
-                    mIsHistoricalPackageUsageAvailable = false;
-                } catch (IOException e) {
-                    Log.w(TAG, "Failed to read package usage times", e);
-                } finally {
-                    IoUtils.closeQuietly(in);
-                }
-            }
-            mLastWritten.set(SystemClock.elapsedRealtime());
-        }
-
-        private void readVersion0LP(InputStream in, StringBuffer sb, String firstLine)
-                throws IOException {
-            // Initial version of the file had no version number and stored one
-            // package-timestamp pair per line.
-            // Note that the first line has already been read from the InputStream.
-            for (String line = firstLine; line != null; line = readLine(in, sb)) {
-                String[] tokens = line.split(" ");
-                if (tokens.length != 2) {
-                    throw new IOException("Failed to parse " + line +
-                            " as package-timestamp pair.");
-                }
-
-                String packageName = tokens[0];
-                PackageParser.Package pkg = mPackages.get(packageName);
-                if (pkg == null) {
-                    continue;
-                }
-
-                long timestamp = parseAsLong(tokens[1]);
-                for (int reason = 0;
-                        reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
-                        reason++) {
-                    pkg.mLastPackageUsageTimeInMills[reason] = timestamp;
-                }
-            }
-        }
-
-        private void readVersion1LP(InputStream in, StringBuffer sb) throws IOException {
-            // Version 1 of the file started with the corresponding version
-            // number and then stored a package name and eight timestamps per line.
-            String line;
-            while ((line = readLine(in, sb)) != null) {
-                String[] tokens = line.split(" ");
-                if (tokens.length != PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT + 1) {
-                    throw new IOException("Failed to parse " + line + " as a timestamp array.");
-                }
-
-                String packageName = tokens[0];
-                PackageParser.Package pkg = mPackages.get(packageName);
-                if (pkg == null) {
-                    continue;
-                }
-
-                for (int reason = 0;
-                        reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
-                        reason++) {
-                    pkg.mLastPackageUsageTimeInMills[reason] = parseAsLong(tokens[reason + 1]);
-                }
-            }
-        }
-
-        private long parseAsLong(String token) throws IOException {
-            try {
-                return Long.parseLong(token);
-            } catch (NumberFormatException e) {
-                throw new IOException("Failed to parse " + token + " as a long.", e);
-            }
-        }
-
-        private String readLine(InputStream in, StringBuffer sb) throws IOException {
-            return readToken(in, sb, '\n');
-        }
-
-        private String readToken(InputStream in, StringBuffer sb, char endOfToken)
-                throws IOException {
-            sb.setLength(0);
-            while (true) {
-                int ch = in.read();
-                if (ch == -1) {
-                    if (sb.length() == 0) {
-                        return null;
-                    }
-                    throw new IOException("Unexpected EOF");
-                }
-                if (ch == endOfToken) {
-                    return sb.toString();
-                }
-                sb.append((char)ch);
-            }
-        }
-
-        private AtomicFile getFile() {
-            File dataDir = Environment.getDataDirectory();
-            File systemDir = new File(dataDir, "system");
-            File fname = new File(systemDir, "package-usage.list");
-            return new AtomicFile(fname);
-        }
-
-        private static final String USAGE_FILE_MAGIC = "PACKAGE_USAGE__VERSION_";
-        private static final String USAGE_FILE_MAGIC_VERSION_1 = USAGE_FILE_MAGIC + "1";
-    }
+    private final CompilerStats mCompilerStats = new CompilerStats();
 
     class PackageHandler extends Handler {
         private boolean mBound = false;
@@ -2217,6 +2015,34 @@
         displayManager.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(metrics);
     }
 
+    /**
+     * Requests that files preopted on a secondary system partition be copied to the data partition
+     * if possible.  Note that the actual copying of the files is accomplished by init for security
+     * reasons. This simply requests that the copy takes place and awaits confirmation of its
+     * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
+     */
+    private static void requestCopyPreoptedFiles() {
+        final int WAIT_TIME_MS = 100;
+        final String CP_PREOPT_PROPERTY = "sys.cppreopt";
+        if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
+            SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
+            // We will wait for up to 100 seconds.
+            final long timeEnd = SystemClock.uptimeMillis() + 100 * 1000;
+            while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
+                try {
+                    Thread.sleep(WAIT_TIME_MS);
+                } catch (InterruptedException e) {
+                    // Do nothing
+                }
+                if (SystemClock.uptimeMillis() > timeEnd) {
+                    SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
+                    Slog.wtf(TAG, "cppreopt did not finish!");
+                    break;
+                }
+            }
+        }
+    }
+
     public PackageManagerService(Context context, Installer installer,
             boolean factoryTest, boolean onlyCore) {
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
@@ -2318,7 +2144,11 @@
 
             mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
 
-            mRestoredSettings = mSettings.readLPw(sUserManager.getUsers(false));
+            mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
+
+            if (mFirstBoot) {
+                requestCopyPreoptedFiles();
+            }
 
             String customResolverActivity = Resources.getSystem().getString(
                     R.string.config_customResolverActivity);
@@ -2669,7 +2499,8 @@
 
             // Now that we know all the packages we are keeping,
             // read and update their last usage times.
-            mPackageUsage.readLP();
+            mPackageUsage.read(mPackages);
+            mCompilerStats.read();
 
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                     SystemClock.uptimeMillis());
@@ -2695,7 +2526,7 @@
             // If this is the first boot or an update from pre-M, and it is a normal
             // boot, then we need to initialize the default preferred apps across
             // all defined users.
-            if (!onlyCore && (mPromoteSystemApps || !mRestoredSettings)) {
+            if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) {
                 for (UserInfo user : sUserManager.getUsers(true)) {
                     mSettings.applyDefaultPreferredAppsLPw(this, user.id);
                     applyFactoryDefaultBrowserLPw(user.id);
@@ -2761,7 +2592,7 @@
                     }
                 }
 
-                int[] stats = performDexOpt(coreApps, false,
+                int[] stats = performDexOptUpgrade(coreApps, false,
                         getCompilerFilterForReason(REASON_CORE_APP));
 
                 final int elapsedTimeSeconds =
@@ -2853,7 +2684,7 @@
 
     @Override
     public boolean isFirstBoot() {
-        return !mRestoredSettings;
+        return mFirstBoot;
     }
 
     @Override
@@ -7268,7 +7099,7 @@
         }
 
         final long startTime = System.nanoTime();
-        final int[] stats = performDexOpt(pkgs, mIsPreNUpgrade /* showDialog */,
+        final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
                     getCompilerFilterForReason(causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT));
 
         final int elapsedTimeSeconds =
@@ -7287,7 +7118,7 @@
      * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped}
      * and {@code numberOfPackagesFailed}.
      */
-    private int[] performDexOpt(List<PackageParser.Package> pkgs, boolean showDialog,
+    private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
             String compilerFilter) {
 
         int numberOfPackagesVisited = 0;
@@ -7321,6 +7152,19 @@
                 }
             }
 
+            // If the OTA updates a system app which was previously preopted to a non-preopted state
+            // the app might end up being verified at runtime. That's because by default the apps
+            // are verify-profile but for preopted apps there's no profile.
+            // Do a hacky check to ensure that if we have no profiles (a reasonable indication
+            // that before the OTA the app was preopted) the app gets compiled with a non-profile
+            // filter (by default interpret-only).
+            // Note that at this stage unused apps are already filtered.
+            if (isSystemApp(pkg) &&
+                    DexFile.isProfileGuidedCompilerFilter(compilerFilter) &&
+                    !Environment.getReferenceProfile(pkg.packageName).exists()) {
+                compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter);
+            }
+
             // checkProfiles is false to avoid merging profiles during boot which
             // might interfere with background compilation (b/28612421).
             // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
@@ -7407,7 +7251,8 @@
                 // Package could not be found. Report failure.
                 return PackageDexOptimizer.DEX_OPT_FAILED;
             }
-            mPackageUsage.write(false);
+            mPackageUsage.maybeWriteAsync(mPackages);
+            mCompilerStats.maybeWriteAsync();
         }
         long callingId = Binder.clearCallingIdentity();
         try {
@@ -7452,11 +7297,12 @@
                 // Currently this will do a full compilation of the library by default.
                 pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
                         false /* checkProfiles */,
-                        getCompilerFilterForReason(REASON_NON_SYSTEM_LIBRARY));
+                        getCompilerFilterForReason(REASON_NON_SYSTEM_LIBRARY),
+                        getOrCreateCompilerPackageStats(depPackage));
             }
         }
         return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets, checkProfiles,
-                targetCompilerFilter);
+                targetCompilerFilter, getOrCreateCompilerPackageStats(p));
     }
 
     Collection<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
@@ -7510,7 +7356,8 @@
     }
 
     public void shutdown() {
-        mPackageUsage.write(true);
+        mPackageUsage.writeNow(mPackages);
+        mCompilerStats.writeNow();
     }
 
     @Override
@@ -15142,7 +14989,8 @@
             // Also, don't fail application installs if the dexopt step fails.
             mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
                     null /* instructionSets */, false /* checkProfiles */,
-                    getCompilerFilterForReason(REASON_INSTALL));
+                    getCompilerFilterForReason(REASON_INSTALL),
+                    getOrCreateCompilerPackageStats(pkg));
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
             // Notify BackgroundDexOptService that the package has been changed.
@@ -18101,6 +17949,7 @@
         public static final int DUMP_DOMAIN_PREFERRED = 1 << 18;
         public static final int DUMP_FROZEN = 1 << 19;
         public static final int DUMP_DEXOPT = 1 << 20;
+        public static final int DUMP_COMPILER_STATS = 1 << 21;
 
         public static final int OPTION_SHOW_FILTERS = 1 << 0;
 
@@ -18218,6 +18067,7 @@
                 pw.println("    installs: details about install sessions");
                 pw.println("    check-permission <permission> <package> [<user>]: does pkg hold perm?");
                 pw.println("    dexopt: dump dexopt state");
+                pw.println("    compiler-stats: dump compiler statistics");
                 pw.println("    <package.name>: info about given package");
                 return;
             } else if ("--checkin".equals(opt)) {
@@ -18339,6 +18189,8 @@
                 dumpState.setDump(DumpState.DUMP_FROZEN);
             } else if ("dexopt".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_DEXOPT);
+            } else if ("compiler-stats".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
             } else if ("write".equals(cmd)) {
                 synchronized (mPackages) {
                     mSettings.writeLPr();
@@ -18701,6 +18553,11 @@
                 dumpDexoptStateLPr(pw, packageName);
             }
 
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
+                if (dumpState.onTitlePrinted()) pw.println();
+                dumpCompilerStatsLPr(pw, packageName);
+            }
+
             if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
                 if (dumpState.onTitlePrinted()) pw.println();
                 mSettings.dumpReadMessagesLPr(pw, dumpState);
@@ -18765,6 +18622,38 @@
         }
     }
 
+    private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) {
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
+        ipw.println();
+        ipw.println("Compiler stats:");
+        ipw.increaseIndent();
+        Collection<PackageParser.Package> packages = null;
+        if (packageName != null) {
+            PackageParser.Package targetPackage = mPackages.get(packageName);
+            if (targetPackage != null) {
+                packages = Collections.singletonList(targetPackage);
+            } else {
+                ipw.println("Unable to find package: " + packageName);
+                return;
+            }
+        } else {
+            packages = mPackages.values();
+        }
+
+        for (PackageParser.Package pkg : packages) {
+            ipw.println("[" + pkg.packageName + "]");
+            ipw.increaseIndent();
+
+            CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.packageName);
+            if (stats == null) {
+                ipw.println("(No recorded stats)");
+            } else {
+                stats.dump(ipw);
+            }
+            ipw.decreaseIndent();
+        }
+    }
+
     private String dumpDomainString(String packageName) {
         List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName)
                 .getList();
@@ -20932,4 +20821,20 @@
         msg.setData(data);
         mProcessLoggingHandler.sendMessage(msg);
     }
+
+    public CompilerStats.PackageStats getCompilerPackageStats(String pkgName) {
+        return mCompilerStats.getPackageStats(pkgName);
+    }
+
+    public CompilerStats.PackageStats getOrCreateCompilerPackageStats(PackageParser.Package pkg) {
+        return getOrCreateCompilerPackageStats(pkg.packageName);
+    }
+
+    public CompilerStats.PackageStats getOrCreateCompilerPackageStats(String pkgName) {
+        return mCompilerStats.getOrCreatePackageStats(pkgName);
+    }
+
+    public void deleteCompilerPackageStats(String pkgName) {
+        mCompilerStats.deletePackageStats(pkgName);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageUsage.java b/services/core/java/com/android/server/pm/PackageUsage.java
new file mode 100644
index 0000000..ac1f739
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageUsage.java
@@ -0,0 +1,199 @@
+/*
+ * 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.pm;
+
+import static android.os.Process.PACKAGE_INFO_GID;
+import static android.os.Process.SYSTEM_UID;
+
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.os.FileUtils;
+import android.util.AtomicFile;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+class PackageUsage extends AbstractStatsBase<Map<String, PackageParser.Package>> {
+
+    private static final String USAGE_FILE_MAGIC = "PACKAGE_USAGE__VERSION_";
+    private static final String USAGE_FILE_MAGIC_VERSION_1 = USAGE_FILE_MAGIC + "1";
+
+    private boolean mIsHistoricalPackageUsageAvailable = true;
+
+    PackageUsage() {
+        super("package-usage.list", "PackageUsage_DiskWriter", /* lock */ true);
+    }
+
+    boolean isHistoricalPackageUsageAvailable() {
+        return mIsHistoricalPackageUsageAvailable;
+    }
+
+    @Override
+    protected void writeInternal(Map<String, PackageParser.Package> packages) {
+        AtomicFile file = getFile();
+        FileOutputStream f = null;
+        try {
+            f = file.startWrite();
+            BufferedOutputStream out = new BufferedOutputStream(f);
+            FileUtils.setPermissions(file.getBaseFile().getPath(),
+                    0640, SYSTEM_UID, PACKAGE_INFO_GID);
+            StringBuilder sb = new StringBuilder();
+
+            sb.append(USAGE_FILE_MAGIC_VERSION_1);
+            sb.append('\n');
+            out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
+
+            for (PackageParser.Package pkg : packages.values()) {
+                if (pkg.getLatestPackageUseTimeInMills() == 0L) {
+                    continue;
+                }
+                sb.setLength(0);
+                sb.append(pkg.packageName);
+                for (long usageTimeInMillis : pkg.mLastPackageUsageTimeInMills) {
+                    sb.append(' ');
+                    sb.append(usageTimeInMillis);
+                }
+                sb.append('\n');
+                out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
+            }
+            out.flush();
+            file.finishWrite(f);
+        } catch (IOException e) {
+            if (f != null) {
+                file.failWrite(f);
+            }
+            Log.e(PackageManagerService.TAG, "Failed to write package usage times", e);
+        }
+    }
+
+    @Override
+    protected void readInternal(Map<String, PackageParser.Package> packages) {
+        AtomicFile file = getFile();
+        BufferedInputStream in = null;
+        try {
+            in = new BufferedInputStream(file.openRead());
+            StringBuffer sb = new StringBuffer();
+
+            String firstLine = readLine(in, sb);
+            if (firstLine == null) {
+                // Empty file. Do nothing.
+            } else if (USAGE_FILE_MAGIC_VERSION_1.equals(firstLine)) {
+                readVersion1LP(packages, in, sb);
+            } else {
+                readVersion0LP(packages, in, sb, firstLine);
+            }
+        } catch (FileNotFoundException expected) {
+            mIsHistoricalPackageUsageAvailable = false;
+        } catch (IOException e) {
+            Log.w(PackageManagerService.TAG, "Failed to read package usage times", e);
+        } finally {
+            IoUtils.closeQuietly(in);
+        }
+    }
+
+    private void readVersion0LP(Map<String, PackageParser.Package> packages, InputStream in,
+            StringBuffer sb, String firstLine)
+            throws IOException {
+        // Initial version of the file had no version number and stored one
+        // package-timestamp pair per line.
+        // Note that the first line has already been read from the InputStream.
+        for (String line = firstLine; line != null; line = readLine(in, sb)) {
+            String[] tokens = line.split(" ");
+            if (tokens.length != 2) {
+                throw new IOException("Failed to parse " + line +
+                        " as package-timestamp pair.");
+            }
+
+            String packageName = tokens[0];
+            PackageParser.Package pkg = packages.get(packageName);
+            if (pkg == null) {
+                continue;
+            }
+
+            long timestamp = parseAsLong(tokens[1]);
+            for (int reason = 0;
+                    reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
+                    reason++) {
+                pkg.mLastPackageUsageTimeInMills[reason] = timestamp;
+            }
+        }
+    }
+
+    private void readVersion1LP(Map<String, PackageParser.Package> packages, InputStream in,
+            StringBuffer sb) throws IOException {
+        // Version 1 of the file started with the corresponding version
+        // number and then stored a package name and eight timestamps per line.
+        String line;
+        while ((line = readLine(in, sb)) != null) {
+            String[] tokens = line.split(" ");
+            if (tokens.length != PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT + 1) {
+                throw new IOException("Failed to parse " + line + " as a timestamp array.");
+            }
+
+            String packageName = tokens[0];
+            PackageParser.Package pkg = packages.get(packageName);
+            if (pkg == null) {
+                continue;
+            }
+
+            for (int reason = 0;
+                    reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
+                    reason++) {
+                pkg.mLastPackageUsageTimeInMills[reason] = parseAsLong(tokens[reason + 1]);
+            }
+        }
+    }
+
+    private long parseAsLong(String token) throws IOException {
+        try {
+            return Long.parseLong(token);
+        } catch (NumberFormatException e) {
+            throw new IOException("Failed to parse " + token + " as a long.", e);
+        }
+    }
+
+    private String readLine(InputStream in, StringBuffer sb) throws IOException {
+        return readToken(in, sb, '\n');
+    }
+
+    private String readToken(InputStream in, StringBuffer sb, char endOfToken)
+            throws IOException {
+        sb.setLength(0);
+        while (true) {
+            int ch = in.read();
+            if (ch == -1) {
+                if (sb.length() == 0) {
+                    return null;
+                }
+                throw new IOException("Unexpected EOF");
+            }
+            if (ch == endOfToken) {
+                return sb.toString();
+            }
+            sb.append((char)ch);
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.h b/services/core/jni/com_android_server_input_InputApplicationHandle.h
index 62c8570..e6f25cc 100644
--- a/services/core/jni/com_android_server_input_InputApplicationHandle.h
+++ b/services/core/jni/com_android_server_input_InputApplicationHandle.h
@@ -26,7 +26,7 @@
 
 class NativeInputApplicationHandle : public InputApplicationHandle {
 public:
-    NativeInputApplicationHandle(jweak objWeak);
+    explicit NativeInputApplicationHandle(jweak objWeak);
     virtual ~NativeInputApplicationHandle();
 
     jobject getInputApplicationHandleObjLocalRef(JNIEnv* env);
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 3674f0f..706b294 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -133,14 +133,8 @@
 
     void setWifiApConfiguration(in WifiConfiguration wifiConfig);
 
-    void addToBlacklist(String bssid);
-
-    void clearBlacklist();
-
     Messenger getWifiServiceMessenger();
 
-    String getConfigFile();
-
     void enableTdls(String remoteIPAddress, boolean enable);
 
     void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable);
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 7c5276c..24cd275 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1266,6 +1266,7 @@
             }
             mTemporarilyDisabledTimestamp = source.mTemporarilyDisabledTimestamp;
             mNetworkSelectionBSSID = source.mNetworkSelectionBSSID;
+            setSeenInLastQualifiedNetworkSelection(source.getSeenInLastQualifiedNetworkSelection());
             setCandidate(source.getCandidate());
             setCandidateScore(source.getCandidateScore());
             setConnectChoice(source.getConnectChoice());
@@ -1406,8 +1407,10 @@
      * @hide
      */
     public boolean isEnterprise() {
-        return allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
-            allowedKeyManagement.get(KeyMgmt.IEEE8021X);
+        return (allowedKeyManagement.get(KeyMgmt.WPA_EAP)
+                || allowedKeyManagement.get(KeyMgmt.IEEE8021X))
+                && enterpriseConfig != null
+                && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE;
     }
 
     @Override
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 08ad35e..c0e8bc2 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -33,7 +33,9 @@
 import java.security.cert.X509Certificate;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -116,7 +118,6 @@
     /** @hide */
     public static final String CA_CERT_ALIAS_DELIMITER = " ";
 
-
     // Fields to copy verbatim from wpa_supplicant.
     private static final String[] SUPPLICANT_CONFIG_KEYS = new String[] {
             IDENTITY_KEY,
@@ -133,6 +134,11 @@
             CA_PATH_KEY
     };
 
+    /**
+     * Fields that have unquoted values in {@link #mFields}.
+     */
+    private static final List<String> UNQUOTED_KEYS = Arrays.asList(ENGINE_KEY, OPP_KEY_CACHING);
+
     private HashMap<String, String> mFields = new HashMap<String, String>();
     private X509Certificate[] mCaCerts;
     private PrivateKey mClientPrivateKey;
@@ -155,6 +161,9 @@
         for (String key : source.mFields.keySet()) {
             mFields.put(key, source.mFields.get(key));
         }
+        mCaCerts = source.mCaCerts;
+        mClientPrivateKey = source.mClientPrivateKey;
+        mClientCertificate = source.mClientCertificate;
         mEapMethod = source.mEapMethod;
         mPhase2Method = source.mPhase2Method;
     }
@@ -455,7 +464,7 @@
             case Eap.AKA:
             case Eap.AKA_PRIME:
                 mEapMethod = eapMethod;
-                mFields.put(OPP_KEY_CACHING, "1");
+                setFieldValue(OPP_KEY_CACHING, "1");
                 break;
             default:
                 throw new IllegalArgumentException("Unknown EAP method");
@@ -514,7 +523,7 @@
      * @return the identity
      */
     public String getIdentity() {
-        return getFieldValue(IDENTITY_KEY, "");
+        return getFieldValue(IDENTITY_KEY);
     }
 
     /**
@@ -523,7 +532,7 @@
      * @param anonymousIdentity the anonymous identity
      */
     public void setAnonymousIdentity(String anonymousIdentity) {
-        setFieldValue(ANON_IDENTITY_KEY, anonymousIdentity, "");
+        setFieldValue(ANON_IDENTITY_KEY, anonymousIdentity);
     }
 
     /**
@@ -531,7 +540,7 @@
      * @return anonymous identity
      */
     public String getAnonymousIdentity() {
-        return getFieldValue(ANON_IDENTITY_KEY, "");
+        return getFieldValue(ANON_IDENTITY_KEY);
     }
 
     /**
@@ -539,7 +548,7 @@
      * @param password the password
      */
     public void setPassword(String password) {
-        setFieldValue(PASSWORD_KEY, password, "");
+        setFieldValue(PASSWORD_KEY, password);
     }
 
     /**
@@ -549,7 +558,7 @@
      * framework, returns "*".
      */
     public String getPassword() {
-        return getFieldValue(PASSWORD_KEY, "");
+        return getFieldValue(PASSWORD_KEY);
     }
 
     /**
@@ -639,7 +648,7 @@
      * @hide
      */
     @Nullable public String[] getCaCertificateAliases() {
-        String value = getFieldValue(CA_CERT_KEY, "");
+        String value = getFieldValue(CA_CERT_KEY);
         if (value.startsWith(CA_CERT_PREFIX)) {
             // Backwards compatibility: parse the original alias prefix.
             return new String[] {getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX)};
@@ -766,7 +775,7 @@
      * @hide
      */
     public String getCaPath() {
-        return getFieldValue(CA_PATH_KEY, "");
+        return getFieldValue(CA_PATH_KEY);
     }
 
     /** Set Client certificate alias.
@@ -782,11 +791,11 @@
         setFieldValue(PRIVATE_KEY_ID_KEY, alias, Credentials.USER_PRIVATE_KEY);
         // Also, set engine parameters
         if (TextUtils.isEmpty(alias)) {
-            mFields.put(ENGINE_KEY, ENGINE_DISABLE);
-            mFields.put(ENGINE_ID_KEY, EMPTY_VALUE);
+            setFieldValue(ENGINE_KEY, ENGINE_DISABLE);
+            setFieldValue(ENGINE_ID_KEY, "");
         } else {
-            mFields.put(ENGINE_KEY, ENGINE_ENABLE);
-            mFields.put(ENGINE_ID_KEY, convertToQuotedString(ENGINE_ID_KEYSTORE));
+            setFieldValue(ENGINE_KEY, ENGINE_ENABLE);
+            setFieldValue(ENGINE_ID_KEY, ENGINE_ID_KEYSTORE);
         }
     }
 
@@ -859,7 +868,7 @@
      * @deprecated in favor of altSubjectMatch
      */
     public void setSubjectMatch(String subjectMatch) {
-        setFieldValue(SUBJECT_MATCH_KEY, subjectMatch, "");
+        setFieldValue(SUBJECT_MATCH_KEY, subjectMatch);
     }
 
     /**
@@ -868,7 +877,7 @@
      * @deprecated in favor of altSubjectMatch
      */
     public String getSubjectMatch() {
-        return getFieldValue(SUBJECT_MATCH_KEY, "");
+        return getFieldValue(SUBJECT_MATCH_KEY);
     }
 
     /**
@@ -878,7 +887,7 @@
      *                     DNS:server.example.com;EMAIL:server@example.com
      */
     public void setAltSubjectMatch(String altSubjectMatch) {
-        setFieldValue(ALTSUBJECT_MATCH_KEY, altSubjectMatch, "");
+        setFieldValue(ALTSUBJECT_MATCH_KEY, altSubjectMatch);
     }
 
     /**
@@ -886,7 +895,7 @@
      * @return the alternate subject match string
      */
     public String getAltSubjectMatch() {
-        return getFieldValue(ALTSUBJECT_MATCH_KEY, "");
+        return getFieldValue(ALTSUBJECT_MATCH_KEY);
     }
 
     /**
@@ -916,7 +925,7 @@
      * @return The domain value.
      */
     public String getDomainSuffixMatch() {
-        return getFieldValue(DOM_SUFFIX_MATCH_KEY, "");
+        return getFieldValue(DOM_SUFFIX_MATCH_KEY);
     }
 
     /**
@@ -925,7 +934,7 @@
      * @param realm the realm
      */
     public void setRealm(String realm) {
-        setFieldValue(REALM_KEY, realm, "");
+        setFieldValue(REALM_KEY, realm);
     }
 
     /**
@@ -933,7 +942,7 @@
      * @return the realm
      */
     public String getRealm() {
-        return getFieldValue(REALM_KEY, "");
+        return getFieldValue(REALM_KEY);
     }
 
     /**
@@ -941,7 +950,7 @@
      * @param plmn the plmn value derived from mcc (mobile country code) & mnc (mobile network code)
      */
     public void setPlmn(String plmn) {
-        setFieldValue(PLMN_KEY, plmn, "");
+        setFieldValue(PLMN_KEY, plmn);
     }
 
     /**
@@ -950,7 +959,7 @@
      * @return the plmn
      */
     public String getPlmn() {
-        return getFieldValue(PLMN_KEY, "");
+        return getFieldValue(PLMN_KEY);
     }
 
     /** See {@link WifiConfiguration#getKeyIdForCredentials} @hide */
@@ -995,13 +1004,13 @@
     }
 
     /**
-     * Returns the field value for the key.
+     * Returns the field value for the key with prefix removed.
      * @param key into the hash
      * @param prefix is the prefix that the value may have
      * @return value
      * @hide
      */
-    public String getFieldValue(String key, String prefix) {
+    private String getFieldValue(String key, String prefix) {
         // TODO: Should raise an exception if |key| is EAP_KEY or PHASE2_KEY since
         // neither of these keys should be retrieved in this manner.
         String value = mFields.get(key);
@@ -1017,23 +1026,15 @@
     }
 
     /**
-     * Set a value with an optional prefix at key
+     * Returns the field value for the key.
      * @param key into the hash
-     * @param value to be set
-     * @param prefix an optional value to be prefixed to actual value
+     * @return value
      * @hide
      */
-    public void setFieldValue(String key, String value, String prefix) {
-        // TODO: Should raise an exception if |key| is EAP_KEY or PHASE2_KEY since
-        // neither of these keys should be set in this manner.
-        if (TextUtils.isEmpty(value)) {
-            mFields.put(key, EMPTY_VALUE);
-        } else {
-            mFields.put(key, convertToQuotedString(prefix + value));
-        }
+    public String getFieldValue(String key) {
+        return getFieldValue(key, "");
     }
 
-
     /**
      * Set a value with an optional prefix at key
      * @param key into the hash
@@ -1041,16 +1042,32 @@
      * @param prefix an optional value to be prefixed to actual value
      * @hide
      */
-    public void setFieldValue(String key, String value) {
+    private void setFieldValue(String key, String value, String prefix) {
         // TODO: Should raise an exception if |key| is EAP_KEY or PHASE2_KEY since
         // neither of these keys should be set in this manner.
         if (TextUtils.isEmpty(value)) {
-           mFields.put(key, EMPTY_VALUE);
+            mFields.put(key, EMPTY_VALUE);
         } else {
-            mFields.put(key, convertToQuotedString(value));
+            String valueToSet;
+            if (!UNQUOTED_KEYS.contains(key)) {
+                valueToSet = convertToQuotedString(prefix + value);
+            } else {
+                valueToSet = prefix + value;
+            }
+            mFields.put(key, valueToSet);
         }
     }
 
+    /**
+     * Set a value at key
+     * @param key into the hash
+     * @param value to be set
+     * @hide
+     */
+    public void setFieldValue(String key, String value) {
+        setFieldValue(key, value, "");
+    }
+
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 19ecbdb..955161c 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1616,41 +1616,6 @@
     }
 
     /**
-     * Add a bssid to the supplicant blacklist
-     *
-     * This API is used by WifiWatchdogService
-     *
-     * @return {@code true} if the operation succeeds else {@code false}
-     * @hide
-     */
-    public boolean addToBlacklist(String bssid) {
-        try {
-            mService.addToBlacklist(bssid);
-            return true;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Clear the supplicant blacklist
-     *
-     * This API is used by WifiWatchdogService
-     *
-     * @return {@code true} if the operation succeeds else {@code false}
-     * @hide
-     */
-    public boolean clearBlacklist() {
-        try {
-            mService.clearBlacklist();
-            return true;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-
-    /**
      * Enable/Disable TDLS on a specific local route.
      *
      * <p>
@@ -2148,18 +2113,6 @@
 
 
     /**
-     * Returns the file in which IP and proxy configuration data is stored
-     * @hide
-     */
-    public String getConfigFile() {
-        try {
-            return mService.getConfigFile();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Allows an application to keep the Wi-Fi radio awake.
      * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
      * Acquiring a WifiLock will keep the radio on until the lock is released.  Multiple
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 716f1d3..3190ead 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -286,6 +286,12 @@
          * {@hide}
          */
         private int mBucketsScanned;
+        /**
+         * Indicates that the scan results received are as a result of a scan of all available
+         * channels. This should only be expected to function for single scans.
+         * {@hide}
+         */
+        private boolean mAllChannelsScanned;
         /** all scan results discovered in this scan, sorted by timestamp in ascending order */
         private ScanResult mResults[];
 
@@ -298,10 +304,12 @@
         }
 
         /** {@hide} */
-        public ScanData(int id, int flags, int bucketsScanned, ScanResult[] results) {
+        public ScanData(int id, int flags, int bucketsScanned, boolean allChannelsScanned,
+                ScanResult[] results) {
             mId = id;
             mFlags = flags;
             mBucketsScanned = bucketsScanned;
+            mAllChannelsScanned = allChannelsScanned;
             mResults = results;
         }
 
@@ -309,6 +317,7 @@
             mId = s.mId;
             mFlags = s.mFlags;
             mBucketsScanned = s.mBucketsScanned;
+            mAllChannelsScanned = s.mAllChannelsScanned;
             mResults = new ScanResult[s.mResults.length];
             for (int i = 0; i < s.mResults.length; i++) {
                 ScanResult result = s.mResults[i];
@@ -330,6 +339,11 @@
             return mBucketsScanned;
         }
 
+        /** {@hide} */
+        public boolean isAllChannelsScanned() {
+            return mAllChannelsScanned;
+        }
+
         public ScanResult[] getResults() {
             return mResults;
         }
@@ -345,6 +359,7 @@
                 dest.writeInt(mId);
                 dest.writeInt(mFlags);
                 dest.writeInt(mBucketsScanned);
+                dest.writeInt(mAllChannelsScanned ? 1 : 0);
                 dest.writeInt(mResults.length);
                 for (int i = 0; i < mResults.length; i++) {
                     ScanResult result = mResults[i];
@@ -362,12 +377,13 @@
                         int id = in.readInt();
                         int flags = in.readInt();
                         int bucketsScanned = in.readInt();
+                        boolean allChannelsScanned = in.readInt() != 0;
                         int n = in.readInt();
                         ScanResult results[] = new ScanResult[n];
                         for (int i = 0; i < n; i++) {
                             results[i] = ScanResult.CREATOR.createFromParcel(in);
                         }
-                        return new ScanData(id, flags, bucketsScanned, results);
+                        return new ScanData(id, flags, bucketsScanned, allChannelsScanned, results);
                     }
 
                     public ScanData[] newArray(int size) {