Merge "Ensure correct install reason"
diff --git a/api/current.txt b/api/current.txt
index 9dafa11..96c47ed 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -32402,7 +32402,7 @@
     ctor public ContactsContract.Intents();
     field public static final java.lang.String ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS = "android.provider.action.VOICE_SEND_MESSAGE_TO_CONTACTS";
     field public static final java.lang.String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE";
-    field public static final java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
+    field public static final deprecated java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
     field public static final java.lang.String EXTRA_CREATE_DESCRIPTION = "com.android.contacts.action.CREATE_DESCRIPTION";
     field public static final java.lang.String EXTRA_FORCE_CREATE = "com.android.contacts.action.FORCE_CREATE";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
@@ -32512,8 +32512,10 @@
   public static final class ContactsContract.ProviderStatus {
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/provider_status";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DATABASE_CREATION_TIMESTAMP = "database_creation_timestamp";
     field public static final java.lang.String STATUS = "status";
     field public static final int STATUS_BUSY = 1; // 0x1
+    field public static final android.net.Uri STATUS_CHANGE_NOTIFICATION_CONTENT_URI;
     field public static final int STATUS_EMPTY = 2; // 0x2
     field public static final int STATUS_NORMAL = 0; // 0x0
   }
@@ -53162,6 +53164,133 @@
 
 }
 
+package java.lang.invoke {
+
+  public class LambdaConversionException extends java.lang.Exception {
+    ctor public LambdaConversionException();
+    ctor public LambdaConversionException(java.lang.String);
+    ctor public LambdaConversionException(java.lang.String, java.lang.Throwable);
+    ctor public LambdaConversionException(java.lang.Throwable);
+    ctor public LambdaConversionException(java.lang.String, java.lang.Throwable, boolean, boolean);
+  }
+
+  public abstract class MethodHandle {
+    method public java.lang.invoke.MethodHandle asFixedArity();
+    method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType);
+    method public java.lang.invoke.MethodHandle asVarargsCollector(java.lang.Class<?>);
+    method public java.lang.invoke.MethodHandle bindTo(java.lang.Object);
+    method public final java.lang.Object invoke(java.lang.Object...) throws java.lang.Throwable;
+    method public final java.lang.Object invokeExact(java.lang.Object...) throws java.lang.Throwable;
+    method public java.lang.Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable;
+    method public boolean isVarargsCollector();
+    method public java.lang.invoke.MethodType type();
+  }
+
+  public abstract interface MethodHandleInfo {
+    method public abstract java.lang.Class<?> getDeclaringClass();
+    method public abstract java.lang.invoke.MethodType getMethodType();
+    method public abstract int getModifiers();
+    method public abstract java.lang.String getName();
+    method public abstract int getReferenceKind();
+    method public default boolean isVarArgs();
+    method public static boolean refKindIsField(int);
+    method public static boolean refKindIsValid(int);
+    method public static java.lang.String refKindName(int);
+    method public static java.lang.String referenceKindToString(int);
+    method public abstract <T extends java.lang.reflect.Member> T reflectAs(java.lang.Class<T>, java.lang.invoke.MethodHandles.Lookup);
+    method public static java.lang.String toString(int, java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType);
+    field public static final int REF_getField = 1; // 0x1
+    field public static final int REF_getStatic = 2; // 0x2
+    field public static final int REF_invokeInterface = 9; // 0x9
+    field public static final int REF_invokeSpecial = 7; // 0x7
+    field public static final int REF_invokeStatic = 6; // 0x6
+    field public static final int REF_invokeVirtual = 5; // 0x5
+    field public static final int REF_newInvokeSpecial = 8; // 0x8
+    field public static final int REF_putField = 3; // 0x3
+    field public static final int REF_putStatic = 4; // 0x4
+  }
+
+  public class MethodHandles {
+    method public static java.lang.invoke.MethodHandle arrayElementGetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle arrayElementSetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, java.lang.Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle constant(java.lang.Class<?>, java.lang.Object);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
+    method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle identity(java.lang.Class<?>);
+    method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandles.Lookup lookup();
+    method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...);
+    method public static java.lang.invoke.MethodHandles.Lookup publicLookup();
+    method public static java.lang.invoke.MethodHandle throwException(java.lang.Class<?>, java.lang.Class<? extends java.lang.Throwable>);
+  }
+
+  public static final class MethodHandles.Lookup {
+    method public java.lang.invoke.MethodHandle bind(java.lang.Object, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findConstructor(java.lang.Class<?>, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findGetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSpecial(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStatic(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStaticGetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findStaticSetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findVirtual(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?>);
+    method public java.lang.Class<?> lookupClass();
+    method public int lookupModes();
+    method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSpecial(java.lang.reflect.Method, java.lang.Class<?>) throws java.lang.IllegalAccessException;
+    field public static final int PACKAGE = 8; // 0x8
+    field public static final int PRIVATE = 2; // 0x2
+    field public static final int PROTECTED = 4; // 0x4
+    field public static final int PUBLIC = 1; // 0x1
+  }
+
+  public final class MethodType implements java.io.Serializable {
+    method public java.lang.invoke.MethodType appendParameterTypes(java.lang.Class<?>...);
+    method public java.lang.invoke.MethodType appendParameterTypes(java.util.List<java.lang.Class<?>>);
+    method public java.lang.invoke.MethodType changeParameterType(int, java.lang.Class<?>);
+    method public java.lang.invoke.MethodType changeReturnType(java.lang.Class<?>);
+    method public java.lang.invoke.MethodType dropParameterTypes(int, int);
+    method public java.lang.invoke.MethodType erase();
+    method public static java.lang.invoke.MethodType fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) throws java.lang.IllegalArgumentException, java.lang.TypeNotPresentException;
+    method public java.lang.invoke.MethodType generic();
+    method public static java.lang.invoke.MethodType genericMethodType(int, boolean);
+    method public static java.lang.invoke.MethodType genericMethodType(int);
+    method public boolean hasPrimitives();
+    method public boolean hasWrappers();
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.lang.Class<?>...);
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>[]);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>, java.lang.Class<?>...);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.invoke.MethodType);
+    method public java.lang.Class<?>[] parameterArray();
+    method public int parameterCount();
+    method public java.util.List<java.lang.Class<?>> parameterList();
+    method public java.lang.Class<?> parameterType(int);
+    method public java.lang.Class<?> returnType();
+    method public java.lang.String toMethodDescriptorString();
+    method public java.lang.invoke.MethodType unwrap();
+    method public java.lang.invoke.MethodType wrap();
+  }
+
+  public class WrongMethodTypeException extends java.lang.RuntimeException {
+    ctor public WrongMethodTypeException();
+    ctor public WrongMethodTypeException(java.lang.String);
+  }
+
+}
+
 package java.lang.ref {
 
   public class PhantomReference<T> extends java.lang.ref.Reference {
diff --git a/api/system-current.txt b/api/system-current.txt
index 63d3b5c..f2e42eb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -21907,6 +21907,7 @@
     method public int getClientPid();
     method public int getClientUid();
     method public int getPlayerInterfaceId();
+    method public android.media.PlayerProxy getPlayerProxy();
     method public int getPlayerState();
     method public int getPlayerType();
     method public void writeToParcel(android.os.Parcel, int);
@@ -23735,6 +23736,13 @@
     field public static final android.os.Parcelable.Creator<android.media.PlaybackParams> CREATOR;
   }
 
+  public class PlayerProxy {
+    method public void pause() throws java.lang.IllegalStateException;
+    method public void setVolume(float) throws java.lang.IllegalStateException;
+    method public void start() throws java.lang.IllegalStateException;
+    method public void stop() throws java.lang.IllegalStateException;
+  }
+
   public final class Rating implements android.os.Parcelable {
     method public int describeContents();
     method public float getPercentRating();
@@ -32748,7 +32756,8 @@
     method public android.os.UserHandle getUserForSerialNumber(long);
     method public java.lang.String getUserName();
     method public java.util.List<android.os.UserHandle> getUserProfiles();
-    method public int getUserRestrictionSource(java.lang.String, android.os.UserHandle);
+    method public deprecated int getUserRestrictionSource(java.lang.String, android.os.UserHandle);
+    method public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(java.lang.String, android.os.UserHandle);
     method public android.os.Bundle getUserRestrictions();
     method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
     method public boolean hasUserRestriction(java.lang.String);
@@ -32814,6 +32823,14 @@
     field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2
   }
 
+  public static final class UserManager.EnforcingUser implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.UserHandle getUserHandle();
+    method public int getUserRestrictionSource();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.os.UserManager.EnforcingUser> CREATOR;
+  }
+
   public static abstract class UserManager.UserRestrictionSource implements java.lang.annotation.Annotation {
   }
 
@@ -35140,7 +35157,7 @@
     ctor public ContactsContract.Intents();
     field public static final java.lang.String ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS = "android.provider.action.VOICE_SEND_MESSAGE_TO_CONTACTS";
     field public static final java.lang.String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE";
-    field public static final java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
+    field public static final deprecated java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
     field public static final java.lang.String EXTRA_CREATE_DESCRIPTION = "com.android.contacts.action.CREATE_DESCRIPTION";
     field public static final java.lang.String EXTRA_FORCE_CREATE = "com.android.contacts.action.FORCE_CREATE";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
@@ -35280,8 +35297,10 @@
   public static final class ContactsContract.ProviderStatus {
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/provider_status";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DATABASE_CREATION_TIMESTAMP = "database_creation_timestamp";
     field public static final java.lang.String STATUS = "status";
     field public static final int STATUS_BUSY = 1; // 0x1
+    field public static final android.net.Uri STATUS_CHANGE_NOTIFICATION_CONTENT_URI;
     field public static final int STATUS_EMPTY = 2; // 0x2
     field public static final int STATUS_NORMAL = 0; // 0x0
   }
@@ -56761,6 +56780,133 @@
 
 }
 
+package java.lang.invoke {
+
+  public class LambdaConversionException extends java.lang.Exception {
+    ctor public LambdaConversionException();
+    ctor public LambdaConversionException(java.lang.String);
+    ctor public LambdaConversionException(java.lang.String, java.lang.Throwable);
+    ctor public LambdaConversionException(java.lang.Throwable);
+    ctor public LambdaConversionException(java.lang.String, java.lang.Throwable, boolean, boolean);
+  }
+
+  public abstract class MethodHandle {
+    method public java.lang.invoke.MethodHandle asFixedArity();
+    method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType);
+    method public java.lang.invoke.MethodHandle asVarargsCollector(java.lang.Class<?>);
+    method public java.lang.invoke.MethodHandle bindTo(java.lang.Object);
+    method public final java.lang.Object invoke(java.lang.Object...) throws java.lang.Throwable;
+    method public final java.lang.Object invokeExact(java.lang.Object...) throws java.lang.Throwable;
+    method public java.lang.Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable;
+    method public boolean isVarargsCollector();
+    method public java.lang.invoke.MethodType type();
+  }
+
+  public abstract interface MethodHandleInfo {
+    method public abstract java.lang.Class<?> getDeclaringClass();
+    method public abstract java.lang.invoke.MethodType getMethodType();
+    method public abstract int getModifiers();
+    method public abstract java.lang.String getName();
+    method public abstract int getReferenceKind();
+    method public default boolean isVarArgs();
+    method public static boolean refKindIsField(int);
+    method public static boolean refKindIsValid(int);
+    method public static java.lang.String refKindName(int);
+    method public static java.lang.String referenceKindToString(int);
+    method public abstract <T extends java.lang.reflect.Member> T reflectAs(java.lang.Class<T>, java.lang.invoke.MethodHandles.Lookup);
+    method public static java.lang.String toString(int, java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType);
+    field public static final int REF_getField = 1; // 0x1
+    field public static final int REF_getStatic = 2; // 0x2
+    field public static final int REF_invokeInterface = 9; // 0x9
+    field public static final int REF_invokeSpecial = 7; // 0x7
+    field public static final int REF_invokeStatic = 6; // 0x6
+    field public static final int REF_invokeVirtual = 5; // 0x5
+    field public static final int REF_newInvokeSpecial = 8; // 0x8
+    field public static final int REF_putField = 3; // 0x3
+    field public static final int REF_putStatic = 4; // 0x4
+  }
+
+  public class MethodHandles {
+    method public static java.lang.invoke.MethodHandle arrayElementGetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle arrayElementSetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, java.lang.Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle constant(java.lang.Class<?>, java.lang.Object);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
+    method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle identity(java.lang.Class<?>);
+    method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandles.Lookup lookup();
+    method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...);
+    method public static java.lang.invoke.MethodHandles.Lookup publicLookup();
+    method public static java.lang.invoke.MethodHandle throwException(java.lang.Class<?>, java.lang.Class<? extends java.lang.Throwable>);
+  }
+
+  public static final class MethodHandles.Lookup {
+    method public java.lang.invoke.MethodHandle bind(java.lang.Object, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findConstructor(java.lang.Class<?>, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findGetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSpecial(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStatic(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStaticGetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findStaticSetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findVirtual(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?>);
+    method public java.lang.Class<?> lookupClass();
+    method public int lookupModes();
+    method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSpecial(java.lang.reflect.Method, java.lang.Class<?>) throws java.lang.IllegalAccessException;
+    field public static final int PACKAGE = 8; // 0x8
+    field public static final int PRIVATE = 2; // 0x2
+    field public static final int PROTECTED = 4; // 0x4
+    field public static final int PUBLIC = 1; // 0x1
+  }
+
+  public final class MethodType implements java.io.Serializable {
+    method public java.lang.invoke.MethodType appendParameterTypes(java.lang.Class<?>...);
+    method public java.lang.invoke.MethodType appendParameterTypes(java.util.List<java.lang.Class<?>>);
+    method public java.lang.invoke.MethodType changeParameterType(int, java.lang.Class<?>);
+    method public java.lang.invoke.MethodType changeReturnType(java.lang.Class<?>);
+    method public java.lang.invoke.MethodType dropParameterTypes(int, int);
+    method public java.lang.invoke.MethodType erase();
+    method public static java.lang.invoke.MethodType fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) throws java.lang.IllegalArgumentException, java.lang.TypeNotPresentException;
+    method public java.lang.invoke.MethodType generic();
+    method public static java.lang.invoke.MethodType genericMethodType(int, boolean);
+    method public static java.lang.invoke.MethodType genericMethodType(int);
+    method public boolean hasPrimitives();
+    method public boolean hasWrappers();
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.lang.Class<?>...);
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>[]);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>, java.lang.Class<?>...);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.invoke.MethodType);
+    method public java.lang.Class<?>[] parameterArray();
+    method public int parameterCount();
+    method public java.util.List<java.lang.Class<?>> parameterList();
+    method public java.lang.Class<?> parameterType(int);
+    method public java.lang.Class<?> returnType();
+    method public java.lang.String toMethodDescriptorString();
+    method public java.lang.invoke.MethodType unwrap();
+    method public java.lang.invoke.MethodType wrap();
+  }
+
+  public class WrongMethodTypeException extends java.lang.RuntimeException {
+    ctor public WrongMethodTypeException();
+    ctor public WrongMethodTypeException(java.lang.String);
+  }
+
+}
+
 package java.lang.ref {
 
   public class PhantomReference<T> extends java.lang.ref.Reference {
diff --git a/api/test-current.txt b/api/test-current.txt
index cc7d595..96ddfc7 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -32516,7 +32516,7 @@
     ctor public ContactsContract.Intents();
     field public static final java.lang.String ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS = "android.provider.action.VOICE_SEND_MESSAGE_TO_CONTACTS";
     field public static final java.lang.String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE";
-    field public static final java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
+    field public static final deprecated java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
     field public static final java.lang.String EXTRA_CREATE_DESCRIPTION = "com.android.contacts.action.CREATE_DESCRIPTION";
     field public static final java.lang.String EXTRA_FORCE_CREATE = "com.android.contacts.action.FORCE_CREATE";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
@@ -32626,8 +32626,10 @@
   public static final class ContactsContract.ProviderStatus {
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/provider_status";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DATABASE_CREATION_TIMESTAMP = "database_creation_timestamp";
     field public static final java.lang.String STATUS = "status";
     field public static final int STATUS_BUSY = 1; // 0x1
+    field public static final android.net.Uri STATUS_CHANGE_NOTIFICATION_CONTENT_URI;
     field public static final int STATUS_EMPTY = 2; // 0x2
     field public static final int STATUS_NORMAL = 0; // 0x0
   }
@@ -53472,6 +53474,133 @@
 
 }
 
+package java.lang.invoke {
+
+  public class LambdaConversionException extends java.lang.Exception {
+    ctor public LambdaConversionException();
+    ctor public LambdaConversionException(java.lang.String);
+    ctor public LambdaConversionException(java.lang.String, java.lang.Throwable);
+    ctor public LambdaConversionException(java.lang.Throwable);
+    ctor public LambdaConversionException(java.lang.String, java.lang.Throwable, boolean, boolean);
+  }
+
+  public abstract class MethodHandle {
+    method public java.lang.invoke.MethodHandle asFixedArity();
+    method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType);
+    method public java.lang.invoke.MethodHandle asVarargsCollector(java.lang.Class<?>);
+    method public java.lang.invoke.MethodHandle bindTo(java.lang.Object);
+    method public final java.lang.Object invoke(java.lang.Object...) throws java.lang.Throwable;
+    method public final java.lang.Object invokeExact(java.lang.Object...) throws java.lang.Throwable;
+    method public java.lang.Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable;
+    method public boolean isVarargsCollector();
+    method public java.lang.invoke.MethodType type();
+  }
+
+  public abstract interface MethodHandleInfo {
+    method public abstract java.lang.Class<?> getDeclaringClass();
+    method public abstract java.lang.invoke.MethodType getMethodType();
+    method public abstract int getModifiers();
+    method public abstract java.lang.String getName();
+    method public abstract int getReferenceKind();
+    method public default boolean isVarArgs();
+    method public static boolean refKindIsField(int);
+    method public static boolean refKindIsValid(int);
+    method public static java.lang.String refKindName(int);
+    method public static java.lang.String referenceKindToString(int);
+    method public abstract <T extends java.lang.reflect.Member> T reflectAs(java.lang.Class<T>, java.lang.invoke.MethodHandles.Lookup);
+    method public static java.lang.String toString(int, java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType);
+    field public static final int REF_getField = 1; // 0x1
+    field public static final int REF_getStatic = 2; // 0x2
+    field public static final int REF_invokeInterface = 9; // 0x9
+    field public static final int REF_invokeSpecial = 7; // 0x7
+    field public static final int REF_invokeStatic = 6; // 0x6
+    field public static final int REF_invokeVirtual = 5; // 0x5
+    field public static final int REF_newInvokeSpecial = 8; // 0x8
+    field public static final int REF_putField = 3; // 0x3
+    field public static final int REF_putStatic = 4; // 0x4
+  }
+
+  public class MethodHandles {
+    method public static java.lang.invoke.MethodHandle arrayElementGetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle arrayElementSetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, java.lang.Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle constant(java.lang.Class<?>, java.lang.Object);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
+    method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle identity(java.lang.Class<?>);
+    method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandles.Lookup lookup();
+    method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...);
+    method public static java.lang.invoke.MethodHandles.Lookup publicLookup();
+    method public static java.lang.invoke.MethodHandle throwException(java.lang.Class<?>, java.lang.Class<? extends java.lang.Throwable>);
+  }
+
+  public static final class MethodHandles.Lookup {
+    method public java.lang.invoke.MethodHandle bind(java.lang.Object, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findConstructor(java.lang.Class<?>, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findGetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSpecial(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStatic(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStaticGetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findStaticSetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findVirtual(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?>);
+    method public java.lang.Class<?> lookupClass();
+    method public int lookupModes();
+    method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSpecial(java.lang.reflect.Method, java.lang.Class<?>) throws java.lang.IllegalAccessException;
+    field public static final int PACKAGE = 8; // 0x8
+    field public static final int PRIVATE = 2; // 0x2
+    field public static final int PROTECTED = 4; // 0x4
+    field public static final int PUBLIC = 1; // 0x1
+  }
+
+  public final class MethodType implements java.io.Serializable {
+    method public java.lang.invoke.MethodType appendParameterTypes(java.lang.Class<?>...);
+    method public java.lang.invoke.MethodType appendParameterTypes(java.util.List<java.lang.Class<?>>);
+    method public java.lang.invoke.MethodType changeParameterType(int, java.lang.Class<?>);
+    method public java.lang.invoke.MethodType changeReturnType(java.lang.Class<?>);
+    method public java.lang.invoke.MethodType dropParameterTypes(int, int);
+    method public java.lang.invoke.MethodType erase();
+    method public static java.lang.invoke.MethodType fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) throws java.lang.IllegalArgumentException, java.lang.TypeNotPresentException;
+    method public java.lang.invoke.MethodType generic();
+    method public static java.lang.invoke.MethodType genericMethodType(int, boolean);
+    method public static java.lang.invoke.MethodType genericMethodType(int);
+    method public boolean hasPrimitives();
+    method public boolean hasWrappers();
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.lang.Class<?>...);
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>[]);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>, java.lang.Class<?>...);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.invoke.MethodType);
+    method public java.lang.Class<?>[] parameterArray();
+    method public int parameterCount();
+    method public java.util.List<java.lang.Class<?>> parameterList();
+    method public java.lang.Class<?> parameterType(int);
+    method public java.lang.Class<?> returnType();
+    method public java.lang.String toMethodDescriptorString();
+    method public java.lang.invoke.MethodType unwrap();
+    method public java.lang.invoke.MethodType wrap();
+  }
+
+  public class WrongMethodTypeException extends java.lang.RuntimeException {
+    ctor public WrongMethodTypeException();
+    ctor public WrongMethodTypeException(java.lang.String);
+  }
+
+}
+
 package java.lang.ref {
 
   public class PhantomReference<T> extends java.lang.ref.Reference {
diff --git a/cmds/svc/src/com/android/commands/svc/NfcCommand.java b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
index 8e9791f..02a92b9 100644
--- a/cmds/svc/src/com/android/commands/svc/NfcCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
@@ -58,7 +58,8 @@
                 IPackageManager pm = IPackageManager.Stub.asInterface(
                         ServiceManager.getService("package"));
                 try {
-                    if (pm.hasSystemFeature(PackageManager.FEATURE_NFC, 0)) {
+                    if (pm.hasSystemFeature(PackageManager.FEATURE_NFC, 0) ||
+			pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, 0)) {
                         INfcAdapter nfc = INfcAdapter.Stub
                                 .asInterface(ServiceManager.getService(Context.NFC_SERVICE));
                         try {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f1f6e7b..2e3c100 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2176,6 +2176,12 @@
             dest.writeParcelable(mContentInsets, 0);
         }
 
+        @Override
+        public String toString() {
+            return "TaskSnapshot{mSnapshot=" + mSnapshot + " mOrientation=" + mOrientation
+                    + " mContentInsets=" + mContentInsets.toShortString();
+        }
+
         public static final Creator<TaskSnapshot> CREATOR = new Creator<TaskSnapshot>() {
             public TaskSnapshot createFromParcel(Parcel source) {
                 return new TaskSnapshot(source);
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index efd2b69..664fbf3 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1984,11 +1984,13 @@
                 if (startIndex != recordNum) {
                     executeOpsTogether(records, isRecordPop, startIndex, recordNum);
                 }
-                // execute all unoptimized together
-                int optimizeEnd;
-                for (optimizeEnd = recordNum + 1; optimizeEnd < numRecords; optimizeEnd++) {
-                    if (records.get(optimizeEnd).mAllowOptimization) {
-                        break;
+                // execute all unoptimized pop operations together or one add operation
+                int optimizeEnd = recordNum + 1;
+                if (isRecordPop.get(recordNum)) {
+                    while (optimizeEnd < numRecords
+                            && isRecordPop.get(optimizeEnd)
+                            && !records.get(optimizeEnd).mAllowOptimization) {
+                        optimizeEnd++;
                     }
                 }
                 executeOpsTogether(records, isRecordPop, recordNum, optimizeEnd);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 21ae853..5824c32 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -31,7 +31,6 @@
 import android.app.ITaskStackListener;
 import android.app.IUiAutomationConnection;
 import android.app.IUidObserver;
-
 import android.app.IUserSwitchObserver;
 import android.app.Notification;
 import android.app.PendingIntent;
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index ef997c9..6deedb6 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -102,4 +102,9 @@
      * been locked.
      */
     void onTaskProfileLocked(int taskId, int userId);
+
+    /**
+     * Called when a task snapshot got updated.
+     */
+    void onTaskSnapshotChanged(int taskId, in ActivityManager.TaskSnapshot snapshot);
 }
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index ad5e69b..fd766bf 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.app.ActivityManager.TaskSnapshot;
 import android.content.ComponentName;
 import android.os.RemoteException;
 
@@ -78,4 +79,9 @@
     @Override
     public void onTaskProfileLocked(int taskId, int userId) {
     }
+
+    @Override
+    public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)
+            throws RemoteException {
+    }
 }
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 9513854..1c2588a 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -19,6 +19,7 @@
 
 import android.os.Bundle;
 import android.os.PersistableBundle;
+import android.os.UserManager;
 import android.content.pm.UserInfo;
 import android.content.IntentSender;
 import android.content.RestrictionEntry;
@@ -61,6 +62,7 @@
     int getUserSerialNumber(int userHandle);
     int getUserHandle(int userSerialNumber);
     int getUserRestrictionSource(String restrictionKey, int userHandle);
+    List<UserManager.EnforcingUser> getUserRestrictionSources(String restrictionKey, int userHandle);
     Bundle getUserRestrictions(int userHandle);
     boolean hasBaseUserRestriction(String restrictionKey, int userHandle);
     boolean hasUserRestriction(in String restrictionKey, int userHandle);
diff --git a/core/java/android/os/UserManager.aidl b/core/java/android/os/UserManager.aidl
new file mode 100644
index 0000000..2611b0f
--- /dev/null
+++ b/core/java/android/os/UserManager.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+parcelable UserManager.EnforcingUser;
\ No newline at end of file
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 3478eaa..efacb20 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1187,7 +1187,9 @@
      * @return The source of user restriction. Any combination of {@link #RESTRICTION_NOT_SET},
      *         {@link #RESTRICTION_SOURCE_SYSTEM}, {@link #RESTRICTION_SOURCE_DEVICE_OWNER}
      *         and {@link #RESTRICTION_SOURCE_PROFILE_OWNER}
+     * @deprecated use {@link #getUserRestrictionSources(String, int)} instead.
      */
+    @Deprecated
     @SystemApi
     @UserRestrictionSource
     public int getUserRestrictionSource(String restrictionKey, UserHandle userHandle) {
@@ -1199,6 +1201,25 @@
     }
 
     /**
+     * @hide
+     *
+     * Returns a list of users who set a user restriction on a given user.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * @param restrictionKey the string key representing the restriction
+     * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+     * @return a list of user ids enforcing this restriction.
+     */
+    @SystemApi
+    public List<EnforcingUser> getUserRestrictionSources(
+            String restrictionKey, UserHandle userHandle) {
+        try {
+            return mService.getUserRestrictionSources(restrictionKey, userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the user-wide restrictions imposed on this user.
      * @return a Bundle containing all the restrictions.
      */
@@ -2310,8 +2331,8 @@
      * @hide
      * Checks if any uninitialized user has the specific seed account name and type.
      *
-     * @param mAccountName The account name to check for
-     * @param mAccountType The account type of the account to check for
+     * @param accountName The account name to check for
+     * @param accountType The account type of the account to check for
      * @return whether the seed account was found
      */
     public boolean someUserHasSeedAccount(String accountName, String accountType) {
@@ -2321,4 +2342,73 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * @hide
+     * User that enforces a restriction.
+     *
+     * @see #getUserRestrictionSources(String, UserHandle)
+     */
+    @SystemApi
+    public static final class EnforcingUser implements Parcelable {
+        private final @UserIdInt int userId;
+        private final @UserRestrictionSource int userRestrictionSource;
+
+        /**
+         * @hide
+         */
+        public EnforcingUser(
+                @UserIdInt int userId, @UserRestrictionSource int userRestrictionSource) {
+            this.userId = userId;
+            this.userRestrictionSource = userRestrictionSource;
+        }
+
+        private EnforcingUser(Parcel in) {
+            userId = in.readInt();
+            userRestrictionSource = in.readInt();
+        }
+
+        public static final Creator<EnforcingUser> CREATOR = new Creator<EnforcingUser>() {
+            @Override
+            public EnforcingUser createFromParcel(Parcel in) {
+                return new EnforcingUser(in);
+            }
+
+            @Override
+            public EnforcingUser[] newArray(int size) {
+                return new EnforcingUser[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(userId);
+            dest.writeInt(userRestrictionSource);
+        }
+
+        /**
+         * Returns an id of the enforcing user.
+         *
+         * <p> Will be UserHandle.USER_NULL when restriction is set by the system.
+         */
+        public UserHandle getUserHandle() {
+            return UserHandle.of(userId);
+        }
+
+        /**
+         * Returns the status of the enforcing user.
+         *
+         * <p> One of {@link #RESTRICTION_SOURCE_SYSTEM},
+         * {@link #RESTRICTION_SOURCE_DEVICE_OWNER} and
+         * {@link #RESTRICTION_SOURCE_PROFILE_OWNER}
+         */
+        public @UserRestrictionSource int getUserRestrictionSource() {
+            return userRestrictionSource;
+        }
+    }
 }
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 466a7e3..97da588 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -15,7 +15,6 @@
  */
 package android.os;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
@@ -24,6 +23,10 @@
  * @hide Only for use within the system server.
  */
 public abstract class UserManagerInternal {
+    public static final int CAMERA_NOT_DISABLED = 0;
+    public static final int CAMERA_DISABLED_LOCALLY = 1;
+    public static final int CAMERA_DISABLED_GLOBALLY = 2;
+
     public interface UserRestrictionsListener {
         /**
          * Called when a user restriction changes.
@@ -36,18 +39,19 @@
     }
 
     /**
-     * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
-     * to set per-user as well as global user restrictions.
+     * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to set
+     * restrictions enforced by the user.
      *
      * @param userId target user id for the local restrictions.
-     * @param localRestrictions per-user restrictions.
-     *     Caller must not change it once passed to this method.
-     * @param globalRestrictions global restrictions set by DO.  Must be null when PO changed user
-     *     restrictions, in which case global restrictions won't change.
-     *     Caller must not change it once passed to this method.
+     * @param restrictions a bundle of user restrictions.
+     * @param isDeviceOwner whether {@code userId} corresponds to device owner user id.
+     * @param cameraRestrictionScope is camera disabled and if so what is the scope of restriction.
+     *        Should be one of {@link #CAMERA_NOT_DISABLED}, {@link #CAMERA_DISABLED_LOCALLY} or
+     *                               {@link #CAMERA_DISABLED_GLOBALLY}
      */
-    public abstract void setDevicePolicyUserRestrictions(int userId,
-            @NonNull Bundle localRestrictions, @Nullable Bundle globalRestrictions);
+    public abstract void setDevicePolicyUserRestrictions(int userId, @Nullable Bundle restrictions,
+            boolean isDeviceOwner, int cameraRestrictionScope);
+
     /**
      * Returns the "base" user restrictions.
      *
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index a07aee5..1b512c6 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8170,11 +8170,29 @@
         /**
          * The content:// style URI for this table.  Requests to this URI can be
          * performed on the UI thread because they are always unblocking.
+         *
+         * <p>Note when you listen on this URI (or any other sub-URIs), you'll be notified for
+         * regular contact change notifications too, which will be sent on the root URI.
+         * If you only want to be notified for provider status change notifications, listen on
+         * {@link #STATUS_CHANGE_NOTIFICATION_CONTENT_URI} instead.
          */
         public static final Uri CONTENT_URI =
                 Uri.withAppendedPath(AUTHORITY_URI, "provider_status");
 
         /**
+         * URI to listen to provider status changes without listening to regular
+         * contact changes.  If a client only wants to monitor {@link ProviderStatus} with
+         * {@link android.app.job.JobScheduler}, then this URI should be used instead of
+         * {@link #CONTENT_URI}, because a job on {@link #CONTENT_URI} will also be invoked
+         * when contacts are changed.
+         *
+         * <p>Note this URI cannot be queried.  A query should be always made on
+         * {@link #CONTENT_URI}.
+         */
+        public static final Uri STATUS_CHANGE_NOTIFICATION_CONTENT_URI =
+                Uri.parse("content://com.android.contacts.provider_status");
+
+        /**
          * The MIME-type of {@link #CONTENT_URI} providing a directory of
          * settings.
          */
@@ -8201,6 +8219,13 @@
          * on the device.
          */
         public static final int STATUS_EMPTY = 2;
+
+        /**
+         * Timestamp (milliseconds since epoch) of when the provider's database was created.
+         *
+         * <P>Type: long
+         */
+        public static final String DATABASE_CREATION_TIMESTAMP = "database_creation_timestamp";
     }
 
     /**
@@ -8768,7 +8793,14 @@
         /**
          * This is the intent that is fired when the contacts database is created. <p> The
          * READ_CONTACT permission is required to receive these broadcasts.
+         *
+         * <p>As of O, this broadcast will no longer be sent.  Applications can use
+         * use {@link android.app.job.JobScheduler} to monitor
+         * {@link ProviderStatus#STATUS_CHANGE_NOTIFICATION_CONTENT_URI}, and read
+         * {@link ProviderStatus#DATABASE_CREATION_TIMESTAMP} to get when
+         * the contacts database was initialized.
          */
+        @Deprecated
         public static final String CONTACTS_DATABASE_CREATED =
                 "android.provider.Contacts.DATABASE_CREATED";
 
diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl
index 894f080..dbca7ff 100644
--- a/core/java/android/webkit/IWebViewUpdateService.aidl
+++ b/core/java/android/webkit/IWebViewUpdateService.aidl
@@ -78,4 +78,14 @@
      * Enable or disable the WebView package fallback mechanism.
      */
     void enableFallbackLogic(boolean enable);
+
+    /**
+     * Used by Settings to determine whether multiprocess is enabled.
+     */
+    boolean isMultiProcessEnabled();
+
+    /**
+     * Used by Settings to enable/disable multiprocess.
+     */
+    void enableMultiProcess(boolean enable);
 }
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
index 6b634df..4150636 100644
--- a/core/jni/android_view_PointerIcon.cpp
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -78,6 +78,9 @@
 
 status_t android_view_PointerIcon_getLoadedIcon(JNIEnv* env, jobject pointerIconObj,
         PointerIcon* outPointerIcon) {
+    if (!pointerIconObj) {
+        return BAD_VALUE;
+    }
     outPointerIcon->style = env->GetIntField(pointerIconObj, gPointerIconClassInfo.mType);
     outPointerIcon->hotSpotX = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotX);
     outPointerIcon->hotSpotY = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotY);
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
index 5e72a0d..0a814f3 100644
--- a/legacy-test/Android.mk
+++ b/legacy-test/Android.mk
@@ -18,7 +18,8 @@
 
 # Build the legacy-test library
 # =============================
-# This contains the junit.framework classes that were in Android API level 25.
+# This contains the junit.framework and android.test classes that were in
+# Android API level 25.
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -28,6 +29,18 @@
 
 include $(BUILD_JAVA_LIBRARY)
 
+# Build the legacy-android-test library
+# =============================
+# This contains the android.test classes that were in Android API level 25.
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src/android)
+LOCAL_MODULE := legacy-android-test
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework junit
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
 ifeq ($(HOST_OS),linux)
 # Build the legacy-performance-test-hostdex library
 # =================================================
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index 1ac5085..7c381ef 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -1,4 +1,4 @@
-// Auto-generated by frameworks/base/tools/localedata/extract_icu_data.py
+// Auto-generated by ./tools/localedata/extract_icu_data.py
 
 const char SCRIPT_CODES[][4] = {
     /* 0  */ {'A', 'h', 'o', 'm'},
@@ -39,27 +39,27 @@
     /* 35 */ {'K', 'h', 'm', 'r'},
     /* 36 */ {'K', 'n', 'd', 'a'},
     /* 37 */ {'K', 'o', 'r', 'e'},
-    /* 38 */ {'K', 't', 'h', 'i'},
-    /* 39 */ {'L', 'a', 'n', 'a'},
-    /* 40 */ {'L', 'a', 'o', 'o'},
-    /* 41 */ {'L', 'a', 't', 'n'},
-    /* 42 */ {'L', 'e', 'p', 'c'},
-    /* 43 */ {'L', 'i', 'n', 'a'},
-    /* 44 */ {'L', 'i', 's', 'u'},
-    /* 45 */ {'L', 'y', 'c', 'i'},
-    /* 46 */ {'L', 'y', 'd', 'i'},
-    /* 47 */ {'M', 'a', 'n', 'd'},
-    /* 48 */ {'M', 'a', 'n', 'i'},
-    /* 49 */ {'M', 'e', 'r', 'c'},
-    /* 50 */ {'M', 'l', 'y', 'm'},
-    /* 51 */ {'M', 'o', 'n', 'g'},
-    /* 52 */ {'M', 'r', 'o', 'o'},
-    /* 53 */ {'M', 'y', 'm', 'r'},
-    /* 54 */ {'N', 'a', 'r', 'b'},
-    /* 55 */ {'N', 'k', 'o', 'o'},
-    /* 56 */ {'O', 'g', 'a', 'm'},
-    /* 57 */ {'O', 'r', 'k', 'h'},
-    /* 58 */ {'O', 'r', 'y', 'a'},
+    /* 38 */ {'L', 'a', 'n', 'a'},
+    /* 39 */ {'L', 'a', 'o', 'o'},
+    /* 40 */ {'L', 'a', 't', 'n'},
+    /* 41 */ {'L', 'e', 'p', 'c'},
+    /* 42 */ {'L', 'i', 'n', 'a'},
+    /* 43 */ {'L', 'i', 's', 'u'},
+    /* 44 */ {'L', 'y', 'c', 'i'},
+    /* 45 */ {'L', 'y', 'd', 'i'},
+    /* 46 */ {'M', 'a', 'n', 'd'},
+    /* 47 */ {'M', 'a', 'n', 'i'},
+    /* 48 */ {'M', 'e', 'r', 'c'},
+    /* 49 */ {'M', 'l', 'y', 'm'},
+    /* 50 */ {'M', 'o', 'n', 'g'},
+    /* 51 */ {'M', 'r', 'o', 'o'},
+    /* 52 */ {'M', 'y', 'm', 'r'},
+    /* 53 */ {'N', 'a', 'r', 'b'},
+    /* 54 */ {'N', 'k', 'o', 'o'},
+    /* 55 */ {'O', 'g', 'a', 'm'},
+    /* 56 */ {'O', 'r', 'k', 'h'},
+    /* 57 */ {'O', 'r', 'y', 'a'},
+    /* 58 */ {'O', 's', 'g', 'e'},
     /* 59 */ {'P', 'a', 'u', 'c'},
     /* 60 */ {'P', 'h', 'l', 'i'},
     /* 61 */ {'P', 'h', 'n', 'x'},
@@ -76,78 +76,147 @@
     /* 72 */ {'T', 'a', 'l', 'e'},
     /* 73 */ {'T', 'a', 'l', 'u'},
     /* 74 */ {'T', 'a', 'm', 'l'},
-    /* 75 */ {'T', 'a', 'v', 't'},
-    /* 76 */ {'T', 'e', 'l', 'u'},
-    /* 77 */ {'T', 'f', 'n', 'g'},
-    /* 78 */ {'T', 'h', 'a', 'a'},
-    /* 79 */ {'T', 'h', 'a', 'i'},
-    /* 80 */ {'T', 'i', 'b', 't'},
-    /* 81 */ {'U', 'g', 'a', 'r'},
-    /* 82 */ {'V', 'a', 'i', 'i'},
-    /* 83 */ {'X', 'p', 'e', 'o'},
-    /* 84 */ {'X', 's', 'u', 'x'},
-    /* 85 */ {'Y', 'i', 'i', 'i'},
-    /* 86 */ {'~', '~', '~', 'A'},
-    /* 87 */ {'~', '~', '~', 'B'},
+    /* 75 */ {'T', 'a', 'n', 'g'},
+    /* 76 */ {'T', 'a', 'v', 't'},
+    /* 77 */ {'T', 'e', 'l', 'u'},
+    /* 78 */ {'T', 'f', 'n', 'g'},
+    /* 79 */ {'T', 'h', 'a', 'a'},
+    /* 80 */ {'T', 'h', 'a', 'i'},
+    /* 81 */ {'T', 'i', 'b', 't'},
+    /* 82 */ {'U', 'g', 'a', 'r'},
+    /* 83 */ {'V', 'a', 'i', 'i'},
+    /* 84 */ {'X', 'p', 'e', 'o'},
+    /* 85 */ {'X', 's', 'u', 'x'},
+    /* 86 */ {'Y', 'i', 'i', 'i'},
+    /* 87 */ {'~', '~', '~', 'A'},
+    /* 88 */ {'~', '~', '~', 'B'},
 };
 
 
 const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
-    {0x61610000u, 41u}, // aa -> Latn
+    {0x61610000u, 40u}, // aa -> Latn
+    {0xA0000000u, 40u}, // aai -> Latn
+    {0xA8000000u, 40u}, // aak -> Latn
+    {0xD0000000u, 40u}, // aau -> Latn
     {0x61620000u, 15u}, // ab -> Cyrl
-    {0xC4200000u, 41u}, // abr -> Latn
-    {0x90400000u, 41u}, // ace -> Latn
-    {0x9C400000u, 41u}, // ach -> Latn
-    {0x80600000u, 41u}, // ada -> Latn
+    {0xA0200000u, 40u}, // abi -> Latn
+    {0xC4200000u, 40u}, // abr -> Latn
+    {0xCC200000u, 40u}, // abt -> Latn
+    {0xE0200000u, 40u}, // aby -> Latn
+    {0x8C400000u, 40u}, // acd -> Latn
+    {0x90400000u, 40u}, // ace -> Latn
+    {0x9C400000u, 40u}, // ach -> Latn
+    {0x80600000u, 40u}, // ada -> Latn
+    {0x90600000u, 40u}, // ade -> Latn
+    {0xA4600000u, 40u}, // adj -> Latn
     {0xE0600000u, 15u}, // ady -> Cyrl
+    {0xE4600000u, 40u}, // adz -> Latn
     {0x61650000u,  4u}, // ae -> Avst
     {0x84800000u,  1u}, // aeb -> Arab
-    {0x61660000u, 41u}, // af -> Latn
-    {0xC0C00000u, 41u}, // agq -> Latn
+    {0xE0800000u, 40u}, // aey -> Latn
+    {0x61660000u, 40u}, // af -> Latn
+    {0x88C00000u, 40u}, // agc -> Latn
+    {0x8CC00000u, 40u}, // agd -> Latn
+    {0x98C00000u, 40u}, // agg -> Latn
+    {0xB0C00000u, 40u}, // agm -> Latn
+    {0xB8C00000u, 40u}, // ago -> Latn
+    {0xC0C00000u, 40u}, // agq -> Latn
+    {0x80E00000u, 40u}, // aha -> Latn
+    {0xACE00000u, 40u}, // ahl -> Latn
     {0xB8E00000u,  0u}, // aho -> Ahom
-    {0x616B0000u, 41u}, // ak -> Latn
-    {0xA9400000u, 84u}, // akk -> Xsux
-    {0xB5600000u, 41u}, // aln -> Latn
+    {0x99200000u, 40u}, // ajg -> Latn
+    {0x616B0000u, 40u}, // ak -> Latn
+    {0xA9400000u, 85u}, // akk -> Xsux
+    {0x81600000u, 40u}, // ala -> Latn
+    {0xA1600000u, 40u}, // ali -> Latn
+    {0xB5600000u, 40u}, // aln -> Latn
     {0xCD600000u, 15u}, // alt -> Cyrl
     {0x616D0000u, 18u}, // am -> Ethi
-    {0xB9800000u, 41u}, // amo -> Latn
-    {0xE5C00000u, 41u}, // aoz -> Latn
+    {0xB1800000u, 40u}, // amm -> Latn
+    {0xB5800000u, 40u}, // amn -> Latn
+    {0xB9800000u, 40u}, // amo -> Latn
+    {0xBD800000u, 40u}, // amp -> Latn
+    {0x89A00000u, 40u}, // anc -> Latn
+    {0xA9A00000u, 40u}, // ank -> Latn
+    {0xB5A00000u, 40u}, // ann -> Latn
+    {0xE1A00000u, 40u}, // any -> Latn
+    {0xA5C00000u, 40u}, // aoj -> Latn
+    {0xB1C00000u, 40u}, // aom -> Latn
+    {0xE5C00000u, 40u}, // aoz -> Latn
+    {0x89E00000u,  1u}, // apc -> Arab
+    {0x8DE00000u,  1u}, // apd -> Arab
+    {0x91E00000u, 40u}, // ape -> Latn
+    {0xC5E00000u, 40u}, // apr -> Latn
+    {0xC9E00000u, 40u}, // aps -> Latn
+    {0xE5E00000u, 40u}, // apz -> Latn
     {0x61720000u,  1u}, // ar -> Arab
-    {0x61725842u, 87u}, // ar-XB -> ~~~B
+    {0x61725842u, 88u}, // ar-XB -> ~~~B
     {0x8A200000u,  2u}, // arc -> Armi
-    {0xB6200000u, 41u}, // arn -> Latn
-    {0xBA200000u, 41u}, // aro -> Latn
+    {0x9E200000u, 40u}, // arh -> Latn
+    {0xB6200000u, 40u}, // arn -> Latn
+    {0xBA200000u, 40u}, // aro -> Latn
     {0xC2200000u,  1u}, // arq -> Arab
     {0xE2200000u,  1u}, // ary -> Arab
     {0xE6200000u,  1u}, // arz -> Arab
     {0x61730000u,  7u}, // as -> Beng
-    {0x82400000u, 41u}, // asa -> Latn
+    {0x82400000u, 40u}, // asa -> Latn
     {0x92400000u, 68u}, // ase -> Sgnw
-    {0xCE400000u, 41u}, // ast -> Latn
-    {0xA6600000u, 41u}, // atj -> Latn
+    {0x9A400000u, 40u}, // asg -> Latn
+    {0xBA400000u, 40u}, // aso -> Latn
+    {0xCE400000u, 40u}, // ast -> Latn
+    {0x82600000u, 40u}, // ata -> Latn
+    {0x9A600000u, 40u}, // atg -> Latn
+    {0xA6600000u, 40u}, // atj -> Latn
+    {0xE2800000u, 40u}, // auy -> Latn
     {0x61760000u, 15u}, // av -> Cyrl
+    {0xAEA00000u,  1u}, // avl -> Arab
+    {0xB6A00000u, 40u}, // avn -> Latn
+    {0xCEA00000u, 40u}, // avt -> Latn
+    {0xD2A00000u, 40u}, // avu -> Latn
     {0x82C00000u, 16u}, // awa -> Deva
-    {0x61790000u, 41u}, // ay -> Latn
-    {0x617A0000u, 41u}, // az -> Latn
+    {0x86C00000u, 40u}, // awb -> Latn
+    {0xBAC00000u, 40u}, // awo -> Latn
+    {0xDEC00000u, 40u}, // awx -> Latn
+    {0x61790000u, 40u}, // ay -> Latn
+    {0x87000000u, 40u}, // ayb -> Latn
+    {0x617A0000u, 40u}, // az -> Latn
     {0x617A4951u,  1u}, // az-IQ -> Arab
     {0x617A4952u,  1u}, // az-IR -> Arab
     {0x617A5255u, 15u}, // az-RU -> Cyrl
     {0x62610000u, 15u}, // ba -> Cyrl
     {0xAC010000u,  1u}, // bal -> Arab
-    {0xB4010000u, 41u}, // ban -> Latn
+    {0xB4010000u, 40u}, // ban -> Latn
     {0xBC010000u, 16u}, // bap -> Deva
-    {0xC4010000u, 41u}, // bar -> Latn
-    {0xC8010000u, 41u}, // bas -> Latn
+    {0xC4010000u, 40u}, // bar -> Latn
+    {0xC8010000u, 40u}, // bas -> Latn
+    {0xD4010000u, 40u}, // bav -> Latn
     {0xDC010000u,  5u}, // bax -> Bamu
-    {0x88210000u, 41u}, // bbc -> Latn
-    {0xA4210000u, 41u}, // bbj -> Latn
-    {0xA0410000u, 41u}, // bci -> Latn
+    {0x80210000u, 40u}, // bba -> Latn
+    {0x84210000u, 40u}, // bbb -> Latn
+    {0x88210000u, 40u}, // bbc -> Latn
+    {0x8C210000u, 40u}, // bbd -> Latn
+    {0xA4210000u, 40u}, // bbj -> Latn
+    {0xBC210000u, 40u}, // bbp -> Latn
+    {0xC4210000u, 40u}, // bbr -> Latn
+    {0x94410000u, 40u}, // bcf -> Latn
+    {0x9C410000u, 40u}, // bch -> Latn
+    {0xA0410000u, 40u}, // bci -> Latn
+    {0xB0410000u, 40u}, // bcm -> Latn
+    {0xB4410000u, 40u}, // bcn -> Latn
+    {0xB8410000u, 40u}, // bco -> Latn
+    {0xC0410000u, 18u}, // bcq -> Ethi
+    {0xD0410000u, 40u}, // bcu -> Latn
+    {0x8C610000u, 40u}, // bdd -> Latn
     {0x62650000u, 15u}, // be -> Cyrl
+    {0x94810000u, 40u}, // bef -> Latn
+    {0x9C810000u, 40u}, // beh -> Latn
     {0xA4810000u,  1u}, // bej -> Arab
-    {0xB0810000u, 41u}, // bem -> Latn
-    {0xD8810000u, 41u}, // bew -> Latn
-    {0xE4810000u, 41u}, // bez -> Latn
-    {0x8CA10000u, 41u}, // bfd -> Latn
+    {0xB0810000u, 40u}, // bem -> Latn
+    {0xCC810000u, 40u}, // bet -> Latn
+    {0xD8810000u, 40u}, // bew -> Latn
+    {0xDC810000u, 40u}, // bex -> Latn
+    {0xE4810000u, 40u}, // bez -> Latn
+    {0x8CA10000u, 40u}, // bfd -> Latn
     {0xC0A10000u, 74u}, // bfq -> Taml
     {0xCCA10000u,  1u}, // bft -> Arab
     {0xE0A10000u, 16u}, // bfy -> Deva
@@ -155,663 +224,1202 @@
     {0x88C10000u, 16u}, // bgc -> Deva
     {0xB4C10000u,  1u}, // bgn -> Arab
     {0xDCC10000u, 21u}, // bgx -> Grek
-    {0x62680000u, 38u}, // bh -> Kthi
     {0x84E10000u, 16u}, // bhb -> Deva
+    {0x98E10000u, 40u}, // bhg -> Latn
     {0xA0E10000u, 16u}, // bhi -> Deva
-    {0xA8E10000u, 41u}, // bhk -> Latn
+    {0xA8E10000u, 40u}, // bhk -> Latn
+    {0xACE10000u, 40u}, // bhl -> Latn
     {0xB8E10000u, 16u}, // bho -> Deva
-    {0x62690000u, 41u}, // bi -> Latn
-    {0xA9010000u, 41u}, // bik -> Latn
-    {0xB5010000u, 41u}, // bin -> Latn
+    {0xE0E10000u, 40u}, // bhy -> Latn
+    {0x62690000u, 40u}, // bi -> Latn
+    {0x85010000u, 40u}, // bib -> Latn
+    {0x99010000u, 40u}, // big -> Latn
+    {0xA9010000u, 40u}, // bik -> Latn
+    {0xB1010000u, 40u}, // bim -> Latn
+    {0xB5010000u, 40u}, // bin -> Latn
+    {0xB9010000u, 40u}, // bio -> Latn
+    {0xC1010000u, 40u}, // biq -> Latn
+    {0x9D210000u, 40u}, // bjh -> Latn
+    {0xA1210000u, 18u}, // bji -> Ethi
     {0xA5210000u, 16u}, // bjj -> Deva
-    {0xB5210000u, 41u}, // bjn -> Latn
-    {0xB1410000u, 41u}, // bkm -> Latn
-    {0xD1410000u, 41u}, // bku -> Latn
-    {0xCD610000u, 75u}, // blt -> Tavt
-    {0x626D0000u, 41u}, // bm -> Latn
-    {0xC1810000u, 41u}, // bmq -> Latn
+    {0xB5210000u, 40u}, // bjn -> Latn
+    {0xB9210000u, 40u}, // bjo -> Latn
+    {0xC5210000u, 40u}, // bjr -> Latn
+    {0xE5210000u, 40u}, // bjz -> Latn
+    {0x89410000u, 40u}, // bkc -> Latn
+    {0xB1410000u, 40u}, // bkm -> Latn
+    {0xC1410000u, 40u}, // bkq -> Latn
+    {0xD1410000u, 40u}, // bku -> Latn
+    {0xD5410000u, 40u}, // bkv -> Latn
+    {0xCD610000u, 76u}, // blt -> Tavt
+    {0x626D0000u, 40u}, // bm -> Latn
+    {0x9D810000u, 40u}, // bmh -> Latn
+    {0xA9810000u, 40u}, // bmk -> Latn
+    {0xC1810000u, 40u}, // bmq -> Latn
+    {0xD1810000u, 40u}, // bmu -> Latn
     {0x626E0000u,  7u}, // bn -> Beng
-    {0x626F0000u, 80u}, // bo -> Tibt
+    {0x99A10000u, 40u}, // bng -> Latn
+    {0xB1A10000u, 40u}, // bnm -> Latn
+    {0xBDA10000u, 40u}, // bnp -> Latn
+    {0x626F0000u, 81u}, // bo -> Tibt
+    {0xA5C10000u, 40u}, // boj -> Latn
+    {0xB1C10000u, 40u}, // bom -> Latn
+    {0xB5C10000u, 40u}, // bon -> Latn
     {0xE1E10000u,  7u}, // bpy -> Beng
+    {0x8A010000u, 40u}, // bqc -> Latn
     {0xA2010000u,  1u}, // bqi -> Arab
-    {0xD6010000u, 41u}, // bqv -> Latn
-    {0x62720000u, 41u}, // br -> Latn
+    {0xBE010000u, 40u}, // bqp -> Latn
+    {0xD6010000u, 40u}, // bqv -> Latn
+    {0x62720000u, 40u}, // br -> Latn
     {0x82210000u, 16u}, // bra -> Deva
     {0x9E210000u,  1u}, // brh -> Arab
     {0xDE210000u, 16u}, // brx -> Deva
-    {0x62730000u, 41u}, // bs -> Latn
+    {0xE6210000u, 40u}, // brz -> Latn
+    {0x62730000u, 40u}, // bs -> Latn
+    {0xA6410000u, 40u}, // bsj -> Latn
     {0xC2410000u,  6u}, // bsq -> Bass
-    {0xCA410000u, 41u}, // bss -> Latn
-    {0xBA610000u, 41u}, // bto -> Latn
+    {0xCA410000u, 40u}, // bss -> Latn
+    {0xCE410000u, 18u}, // bst -> Ethi
+    {0xBA610000u, 40u}, // bto -> Latn
+    {0xCE610000u, 40u}, // btt -> Latn
     {0xD6610000u, 16u}, // btv -> Deva
     {0x82810000u, 15u}, // bua -> Cyrl
-    {0x8A810000u, 41u}, // buc -> Latn
-    {0x9A810000u, 41u}, // bug -> Latn
-    {0xB2810000u, 41u}, // bum -> Latn
-    {0x86A10000u, 41u}, // bvb -> Latn
+    {0x8A810000u, 40u}, // buc -> Latn
+    {0x8E810000u, 40u}, // bud -> Latn
+    {0x9A810000u, 40u}, // bug -> Latn
+    {0xAA810000u, 40u}, // buk -> Latn
+    {0xB2810000u, 40u}, // bum -> Latn
+    {0xBA810000u, 40u}, // buo -> Latn
+    {0xCA810000u, 40u}, // bus -> Latn
+    {0xD2810000u, 40u}, // buu -> Latn
+    {0x86A10000u, 40u}, // bvb -> Latn
+    {0x8EC10000u, 40u}, // bwd -> Latn
+    {0xC6C10000u, 40u}, // bwr -> Latn
+    {0x9EE10000u, 40u}, // bxh -> Latn
+    {0x93010000u, 40u}, // bye -> Latn
     {0xB7010000u, 18u}, // byn -> Ethi
-    {0xD7010000u, 41u}, // byv -> Latn
-    {0x93210000u, 41u}, // bze -> Latn
-    {0x63610000u, 41u}, // ca -> Latn
-    {0x9C420000u, 41u}, // cch -> Latn
+    {0xC7010000u, 40u}, // byr -> Latn
+    {0xCB010000u, 40u}, // bys -> Latn
+    {0xD7010000u, 40u}, // byv -> Latn
+    {0xDF010000u, 40u}, // byx -> Latn
+    {0x83210000u, 40u}, // bza -> Latn
+    {0x93210000u, 40u}, // bze -> Latn
+    {0x97210000u, 40u}, // bzf -> Latn
+    {0x9F210000u, 40u}, // bzh -> Latn
+    {0xDB210000u, 40u}, // bzw -> Latn
+    {0x63610000u, 40u}, // ca -> Latn
+    {0xB4020000u, 40u}, // can -> Latn
+    {0xA4220000u, 40u}, // cbj -> Latn
+    {0x9C420000u, 40u}, // cch -> Latn
     {0xBC420000u,  7u}, // ccp -> Beng
     {0x63650000u, 15u}, // ce -> Cyrl
-    {0x84820000u, 41u}, // ceb -> Latn
-    {0x98C20000u, 41u}, // cgg -> Latn
-    {0x63680000u, 41u}, // ch -> Latn
-    {0xA8E20000u, 41u}, // chk -> Latn
+    {0x84820000u, 40u}, // ceb -> Latn
+    {0x80A20000u, 40u}, // cfa -> Latn
+    {0x98C20000u, 40u}, // cgg -> Latn
+    {0x63680000u, 40u}, // ch -> Latn
+    {0xA8E20000u, 40u}, // chk -> Latn
     {0xB0E20000u, 15u}, // chm -> Cyrl
-    {0xB8E20000u, 41u}, // cho -> Latn
-    {0xBCE20000u, 41u}, // chp -> Latn
+    {0xB8E20000u, 40u}, // cho -> Latn
+    {0xBCE20000u, 40u}, // chp -> Latn
     {0xC4E20000u, 12u}, // chr -> Cher
     {0x81220000u,  1u}, // cja -> Arab
     {0xB1220000u, 11u}, // cjm -> Cham
+    {0xD5220000u, 40u}, // cjv -> Latn
     {0x85420000u,  1u}, // ckb -> Arab
-    {0x636F0000u, 41u}, // co -> Latn
+    {0xAD420000u, 40u}, // ckl -> Latn
+    {0xB9420000u, 40u}, // cko -> Latn
+    {0xE1420000u, 40u}, // cky -> Latn
+    {0x81620000u, 40u}, // cla -> Latn
+    {0x91820000u, 40u}, // cme -> Latn
+    {0x636F0000u, 40u}, // co -> Latn
     {0xBDC20000u, 13u}, // cop -> Copt
-    {0xC9E20000u, 41u}, // cps -> Latn
+    {0xC9E20000u, 40u}, // cps -> Latn
     {0x63720000u,  9u}, // cr -> Cans
     {0xA6220000u,  9u}, // crj -> Cans
     {0xAA220000u,  9u}, // crk -> Cans
     {0xAE220000u,  9u}, // crl -> Cans
     {0xB2220000u,  9u}, // crm -> Cans
-    {0xCA220000u, 41u}, // crs -> Latn
-    {0x63730000u, 41u}, // cs -> Latn
-    {0x86420000u, 41u}, // csb -> Latn
+    {0xCA220000u, 40u}, // crs -> Latn
+    {0x63730000u, 40u}, // cs -> Latn
+    {0x86420000u, 40u}, // csb -> Latn
     {0xDA420000u,  9u}, // csw -> Cans
     {0x8E620000u, 59u}, // ctd -> Pauc
     {0x63750000u, 15u}, // cu -> Cyrl
     {0x63760000u, 15u}, // cv -> Cyrl
-    {0x63790000u, 41u}, // cy -> Latn
-    {0x64610000u, 41u}, // da -> Latn
-    {0xA8030000u, 41u}, // dak -> Latn
+    {0x63790000u, 40u}, // cy -> Latn
+    {0x64610000u, 40u}, // da -> Latn
+    {0x8C030000u, 40u}, // dad -> Latn
+    {0x94030000u, 40u}, // daf -> Latn
+    {0x98030000u, 40u}, // dag -> Latn
+    {0x9C030000u, 40u}, // dah -> Latn
+    {0xA8030000u, 40u}, // dak -> Latn
     {0xC4030000u, 15u}, // dar -> Cyrl
-    {0xD4030000u, 41u}, // dav -> Latn
+    {0xD4030000u, 40u}, // dav -> Latn
+    {0x8C230000u, 40u}, // dbd -> Latn
+    {0xC0230000u, 40u}, // dbq -> Latn
     {0x88430000u,  1u}, // dcc -> Arab
-    {0x64650000u, 41u}, // de -> Latn
-    {0xB4830000u, 41u}, // den -> Latn
-    {0xC4C30000u, 41u}, // dgr -> Latn
-    {0x91230000u, 41u}, // dje -> Latn
-    {0xA5A30000u, 41u}, // dnj -> Latn
+    {0xB4630000u, 40u}, // ddn -> Latn
+    {0x64650000u, 40u}, // de -> Latn
+    {0x8C830000u, 40u}, // ded -> Latn
+    {0xB4830000u, 40u}, // den -> Latn
+    {0x80C30000u, 40u}, // dga -> Latn
+    {0x9CC30000u, 40u}, // dgh -> Latn
+    {0xA0C30000u, 40u}, // dgi -> Latn
+    {0xACC30000u,  1u}, // dgl -> Arab
+    {0xC4C30000u, 40u}, // dgr -> Latn
+    {0xE4C30000u, 40u}, // dgz -> Latn
+    {0x81030000u, 40u}, // dia -> Latn
+    {0x91230000u, 40u}, // dje -> Latn
+    {0xA5A30000u, 40u}, // dnj -> Latn
+    {0x85C30000u, 40u}, // dob -> Latn
     {0xA1C30000u,  1u}, // doi -> Arab
-    {0x86430000u, 41u}, // dsb -> Latn
-    {0xB2630000u, 41u}, // dtm -> Latn
-    {0xBE630000u, 41u}, // dtp -> Latn
-    {0x82830000u, 41u}, // dua -> Latn
-    {0x64760000u, 78u}, // dv -> Thaa
-    {0xBB030000u, 41u}, // dyo -> Latn
-    {0xD3030000u, 41u}, // dyu -> Latn
-    {0x647A0000u, 80u}, // dz -> Tibt
-    {0xD0240000u, 41u}, // ebu -> Latn
-    {0x65650000u, 41u}, // ee -> Latn
-    {0xA0A40000u, 41u}, // efi -> Latn
-    {0xACC40000u, 41u}, // egl -> Latn
+    {0xBDC30000u, 40u}, // dop -> Latn
+    {0xD9C30000u, 40u}, // dow -> Latn
+    {0xA2230000u, 40u}, // dri -> Latn
+    {0xCA230000u, 18u}, // drs -> Ethi
+    {0x86430000u, 40u}, // dsb -> Latn
+    {0xB2630000u, 40u}, // dtm -> Latn
+    {0xBE630000u, 40u}, // dtp -> Latn
+    {0xCA630000u, 40u}, // dts -> Latn
+    {0xE2630000u, 16u}, // dty -> Deva
+    {0x82830000u, 40u}, // dua -> Latn
+    {0x8A830000u, 40u}, // duc -> Latn
+    {0x8E830000u, 40u}, // dud -> Latn
+    {0x9A830000u, 40u}, // dug -> Latn
+    {0x64760000u, 79u}, // dv -> Thaa
+    {0x82A30000u, 40u}, // dva -> Latn
+    {0xDAC30000u, 40u}, // dww -> Latn
+    {0xBB030000u, 40u}, // dyo -> Latn
+    {0xD3030000u, 40u}, // dyu -> Latn
+    {0x647A0000u, 81u}, // dz -> Tibt
+    {0x9B230000u, 40u}, // dzg -> Latn
+    {0xD0240000u, 40u}, // ebu -> Latn
+    {0x65650000u, 40u}, // ee -> Latn
+    {0xA0A40000u, 40u}, // efi -> Latn
+    {0xACC40000u, 40u}, // egl -> Latn
     {0xE0C40000u, 17u}, // egy -> Egyp
     {0xE1440000u, 32u}, // eky -> Kali
     {0x656C0000u, 21u}, // el -> Grek
-    {0x656E0000u, 41u}, // en -> Latn
-    {0x656E5841u, 86u}, // en-XA -> ~~~A
-    {0x656F0000u, 41u}, // eo -> Latn
-    {0x65730000u, 41u}, // es -> Latn
-    {0xD2440000u, 41u}, // esu -> Latn
-    {0x65740000u, 41u}, // et -> Latn
+    {0x81840000u, 40u}, // ema -> Latn
+    {0xA1840000u, 40u}, // emi -> Latn
+    {0x656E0000u, 40u}, // en -> Latn
+    {0x656E5841u, 87u}, // en-XA -> ~~~A
+    {0xB5A40000u, 40u}, // enn -> Latn
+    {0xC1A40000u, 40u}, // enq -> Latn
+    {0x656F0000u, 40u}, // eo -> Latn
+    {0xA2240000u, 40u}, // eri -> Latn
+    {0x65730000u, 40u}, // es -> Latn
+    {0xD2440000u, 40u}, // esu -> Latn
+    {0x65740000u, 40u}, // et -> Latn
+    {0xC6640000u, 40u}, // etr -> Latn
     {0xCE640000u, 30u}, // ett -> Ital
-    {0x65750000u, 41u}, // eu -> Latn
-    {0xBAC40000u, 41u}, // ewo -> Latn
-    {0xCEE40000u, 41u}, // ext -> Latn
+    {0xD2640000u, 40u}, // etu -> Latn
+    {0xDE640000u, 40u}, // etx -> Latn
+    {0x65750000u, 40u}, // eu -> Latn
+    {0xBAC40000u, 40u}, // ewo -> Latn
+    {0xCEE40000u, 40u}, // ext -> Latn
     {0x66610000u,  1u}, // fa -> Arab
-    {0xB4050000u, 41u}, // fan -> Latn
-    {0x66660000u, 41u}, // ff -> Latn
-    {0xB0A50000u, 41u}, // ffm -> Latn
-    {0x66690000u, 41u}, // fi -> Latn
+    {0x80050000u, 40u}, // faa -> Latn
+    {0x84050000u, 40u}, // fab -> Latn
+    {0x98050000u, 40u}, // fag -> Latn
+    {0xA0050000u, 40u}, // fai -> Latn
+    {0xB4050000u, 40u}, // fan -> Latn
+    {0x66660000u, 40u}, // ff -> Latn
+    {0xA0A50000u, 40u}, // ffi -> Latn
+    {0xB0A50000u, 40u}, // ffm -> Latn
+    {0x66690000u, 40u}, // fi -> Latn
     {0x81050000u,  1u}, // fia -> Arab
-    {0xAD050000u, 41u}, // fil -> Latn
-    {0xCD050000u, 41u}, // fit -> Latn
-    {0x666A0000u, 41u}, // fj -> Latn
-    {0x666F0000u, 41u}, // fo -> Latn
-    {0xB5C50000u, 41u}, // fon -> Latn
-    {0x66720000u, 41u}, // fr -> Latn
-    {0x8A250000u, 41u}, // frc -> Latn
-    {0xBE250000u, 41u}, // frp -> Latn
-    {0xC6250000u, 41u}, // frr -> Latn
-    {0xCA250000u, 41u}, // frs -> Latn
-    {0x8E850000u, 41u}, // fud -> Latn
-    {0xC2850000u, 41u}, // fuq -> Latn
-    {0xC6850000u, 41u}, // fur -> Latn
-    {0xD6850000u, 41u}, // fuv -> Latn
-    {0xC6A50000u, 41u}, // fvr -> Latn
-    {0x66790000u, 41u}, // fy -> Latn
-    {0x67610000u, 41u}, // ga -> Latn
-    {0x80060000u, 41u}, // gaa -> Latn
-    {0x98060000u, 41u}, // gag -> Latn
+    {0xAD050000u, 40u}, // fil -> Latn
+    {0xCD050000u, 40u}, // fit -> Latn
+    {0x666A0000u, 40u}, // fj -> Latn
+    {0xC5650000u, 40u}, // flr -> Latn
+    {0xBD850000u, 40u}, // fmp -> Latn
+    {0x666F0000u, 40u}, // fo -> Latn
+    {0x8DC50000u, 40u}, // fod -> Latn
+    {0xB5C50000u, 40u}, // fon -> Latn
+    {0xC5C50000u, 40u}, // for -> Latn
+    {0x91E50000u, 40u}, // fpe -> Latn
+    {0xCA050000u, 40u}, // fqs -> Latn
+    {0x66720000u, 40u}, // fr -> Latn
+    {0x8A250000u, 40u}, // frc -> Latn
+    {0xBE250000u, 40u}, // frp -> Latn
+    {0xC6250000u, 40u}, // frr -> Latn
+    {0xCA250000u, 40u}, // frs -> Latn
+    {0x86850000u,  1u}, // fub -> Arab
+    {0x8E850000u, 40u}, // fud -> Latn
+    {0x92850000u, 40u}, // fue -> Latn
+    {0x96850000u, 40u}, // fuf -> Latn
+    {0x9E850000u, 40u}, // fuh -> Latn
+    {0xC2850000u, 40u}, // fuq -> Latn
+    {0xC6850000u, 40u}, // fur -> Latn
+    {0xD6850000u, 40u}, // fuv -> Latn
+    {0xE2850000u, 40u}, // fuy -> Latn
+    {0xC6A50000u, 40u}, // fvr -> Latn
+    {0x66790000u, 40u}, // fy -> Latn
+    {0x67610000u, 40u}, // ga -> Latn
+    {0x80060000u, 40u}, // gaa -> Latn
+    {0x94060000u, 40u}, // gaf -> Latn
+    {0x98060000u, 40u}, // gag -> Latn
+    {0x9C060000u, 40u}, // gah -> Latn
+    {0xA4060000u, 40u}, // gaj -> Latn
+    {0xB0060000u, 40u}, // gam -> Latn
     {0xB4060000u, 24u}, // gan -> Hans
-    {0xE0060000u, 41u}, // gay -> Latn
+    {0xD8060000u, 40u}, // gaw -> Latn
+    {0xE0060000u, 40u}, // gay -> Latn
+    {0x94260000u, 40u}, // gbf -> Latn
     {0xB0260000u, 16u}, // gbm -> Deva
+    {0xE0260000u, 40u}, // gby -> Latn
     {0xE4260000u,  1u}, // gbz -> Arab
-    {0xC4460000u, 41u}, // gcr -> Latn
-    {0x67640000u, 41u}, // gd -> Latn
+    {0xC4460000u, 40u}, // gcr -> Latn
+    {0x67640000u, 40u}, // gd -> Latn
+    {0x90660000u, 40u}, // gde -> Latn
+    {0xB4660000u, 40u}, // gdn -> Latn
+    {0xC4660000u, 40u}, // gdr -> Latn
+    {0x84860000u, 40u}, // geb -> Latn
+    {0xA4860000u, 40u}, // gej -> Latn
+    {0xAC860000u, 40u}, // gel -> Latn
     {0xE4860000u, 18u}, // gez -> Ethi
+    {0xA8A60000u, 40u}, // gfk -> Latn
     {0xB4C60000u, 16u}, // ggn -> Deva
-    {0xAD060000u, 41u}, // gil -> Latn
+    {0xC8E60000u, 40u}, // ghs -> Latn
+    {0xAD060000u, 40u}, // gil -> Latn
+    {0xB1060000u, 40u}, // gim -> Latn
     {0xA9260000u,  1u}, // gjk -> Arab
+    {0xB5260000u, 40u}, // gjn -> Latn
     {0xD1260000u,  1u}, // gju -> Arab
-    {0x676C0000u, 41u}, // gl -> Latn
+    {0xB5460000u, 40u}, // gkn -> Latn
+    {0xBD460000u, 40u}, // gkp -> Latn
+    {0x676C0000u, 40u}, // gl -> Latn
     {0xA9660000u,  1u}, // glk -> Arab
-    {0x676E0000u, 41u}, // gn -> Latn
+    {0xB1860000u, 40u}, // gmm -> Latn
+    {0xD5860000u, 18u}, // gmv -> Ethi
+    {0x676E0000u, 40u}, // gn -> Latn
+    {0x8DA60000u, 40u}, // gnd -> Latn
+    {0x99A60000u, 40u}, // gng -> Latn
+    {0x8DC60000u, 40u}, // god -> Latn
+    {0x95C60000u, 18u}, // gof -> Ethi
+    {0xA1C60000u, 40u}, // goi -> Latn
     {0xB1C60000u, 16u}, // gom -> Deva
-    {0xB5C60000u, 76u}, // gon -> Telu
-    {0xC5C60000u, 41u}, // gor -> Latn
-    {0xC9C60000u, 41u}, // gos -> Latn
+    {0xB5C60000u, 77u}, // gon -> Telu
+    {0xC5C60000u, 40u}, // gor -> Latn
+    {0xC9C60000u, 40u}, // gos -> Latn
     {0xCDC60000u, 20u}, // got -> Goth
     {0x8A260000u, 14u}, // grc -> Cprt
     {0xCE260000u,  7u}, // grt -> Beng
-    {0xDA460000u, 41u}, // gsw -> Latn
+    {0xDA260000u, 40u}, // grw -> Latn
+    {0xDA460000u, 40u}, // gsw -> Latn
     {0x67750000u, 22u}, // gu -> Gujr
-    {0x86860000u, 41u}, // gub -> Latn
-    {0x8A860000u, 41u}, // guc -> Latn
-    {0xC6860000u, 41u}, // gur -> Latn
-    {0xE6860000u, 41u}, // guz -> Latn
-    {0x67760000u, 41u}, // gv -> Latn
+    {0x86860000u, 40u}, // gub -> Latn
+    {0x8A860000u, 40u}, // guc -> Latn
+    {0x8E860000u, 40u}, // gud -> Latn
+    {0xC6860000u, 40u}, // gur -> Latn
+    {0xDA860000u, 40u}, // guw -> Latn
+    {0xDE860000u, 40u}, // gux -> Latn
+    {0xE6860000u, 40u}, // guz -> Latn
+    {0x67760000u, 40u}, // gv -> Latn
+    {0x96A60000u, 40u}, // gvf -> Latn
     {0xC6A60000u, 16u}, // gvr -> Deva
-    {0xA2C60000u, 41u}, // gwi -> Latn
-    {0x68610000u, 41u}, // ha -> Latn
+    {0xCAA60000u, 40u}, // gvs -> Latn
+    {0x8AC60000u,  1u}, // gwc -> Arab
+    {0xA2C60000u, 40u}, // gwi -> Latn
+    {0xCEC60000u,  1u}, // gwt -> Arab
+    {0xA3060000u, 40u}, // gyi -> Latn
+    {0x68610000u, 40u}, // ha -> Latn
     {0x6861434Du,  1u}, // ha-CM -> Arab
     {0x68615344u,  1u}, // ha-SD -> Arab
+    {0x98070000u, 40u}, // hag -> Latn
     {0xA8070000u, 24u}, // hak -> Hans
-    {0xD8070000u, 41u}, // haw -> Latn
+    {0xB0070000u, 40u}, // ham -> Latn
+    {0xD8070000u, 40u}, // haw -> Latn
     {0xE4070000u,  1u}, // haz -> Arab
+    {0x84270000u, 40u}, // hbb -> Latn
+    {0xE0670000u, 18u}, // hdy -> Ethi
     {0x68650000u, 27u}, // he -> Hebr
+    {0xE0E70000u, 40u}, // hhy -> Latn
     {0x68690000u, 16u}, // hi -> Deva
-    {0x95070000u, 41u}, // hif -> Latn
-    {0xAD070000u, 41u}, // hil -> Latn
+    {0x81070000u, 40u}, // hia -> Latn
+    {0x95070000u, 40u}, // hif -> Latn
+    {0x99070000u, 40u}, // hig -> Latn
+    {0x9D070000u, 40u}, // hih -> Latn
+    {0xAD070000u, 40u}, // hil -> Latn
+    {0x81670000u, 40u}, // hla -> Latn
     {0xD1670000u, 28u}, // hlu -> Hluw
     {0x8D870000u, 62u}, // hmd -> Plrd
+    {0xCD870000u, 40u}, // hmt -> Latn
     {0x8DA70000u,  1u}, // hnd -> Arab
     {0x91A70000u, 16u}, // hne -> Deva
     {0xA5A70000u, 29u}, // hnj -> Hmng
-    {0xB5A70000u, 41u}, // hnn -> Latn
+    {0xB5A70000u, 40u}, // hnn -> Latn
     {0xB9A70000u,  1u}, // hno -> Arab
-    {0x686F0000u, 41u}, // ho -> Latn
+    {0x686F0000u, 40u}, // ho -> Latn
     {0x89C70000u, 16u}, // hoc -> Deva
     {0xA5C70000u, 16u}, // hoj -> Deva
-    {0x68720000u, 41u}, // hr -> Latn
-    {0x86470000u, 41u}, // hsb -> Latn
+    {0xCDC70000u, 40u}, // hot -> Latn
+    {0x68720000u, 40u}, // hr -> Latn
+    {0x86470000u, 40u}, // hsb -> Latn
     {0xB6470000u, 24u}, // hsn -> Hans
-    {0x68740000u, 41u}, // ht -> Latn
-    {0x68750000u, 41u}, // hu -> Latn
+    {0x68740000u, 40u}, // ht -> Latn
+    {0x68750000u, 40u}, // hu -> Latn
+    {0xA2870000u, 40u}, // hui -> Latn
     {0x68790000u,  3u}, // hy -> Armn
-    {0x687A0000u, 41u}, // hz -> Latn
-    {0x69610000u, 41u}, // ia -> Latn
-    {0x80280000u, 41u}, // iba -> Latn
-    {0x84280000u, 41u}, // ibb -> Latn
-    {0x69640000u, 41u}, // id -> Latn
-    {0x69670000u, 41u}, // ig -> Latn
-    {0x69690000u, 85u}, // ii -> Yiii
-    {0x696B0000u, 41u}, // ik -> Latn
-    {0xCD480000u, 41u}, // ikt -> Latn
-    {0xB9680000u, 41u}, // ilo -> Latn
-    {0x696E0000u, 41u}, // in -> Latn
+    {0x687A0000u, 40u}, // hz -> Latn
+    {0x69610000u, 40u}, // ia -> Latn
+    {0xB4080000u, 40u}, // ian -> Latn
+    {0xC4080000u, 40u}, // iar -> Latn
+    {0x80280000u, 40u}, // iba -> Latn
+    {0x84280000u, 40u}, // ibb -> Latn
+    {0xE0280000u, 40u}, // iby -> Latn
+    {0x80480000u, 40u}, // ica -> Latn
+    {0x9C480000u, 40u}, // ich -> Latn
+    {0x69640000u, 40u}, // id -> Latn
+    {0x8C680000u, 40u}, // idd -> Latn
+    {0xA0680000u, 40u}, // idi -> Latn
+    {0xD0680000u, 40u}, // idu -> Latn
+    {0x69670000u, 40u}, // ig -> Latn
+    {0x84C80000u, 40u}, // igb -> Latn
+    {0x90C80000u, 40u}, // ige -> Latn
+    {0x69690000u, 86u}, // ii -> Yiii
+    {0xA5280000u, 40u}, // ijj -> Latn
+    {0x696B0000u, 40u}, // ik -> Latn
+    {0xA9480000u, 40u}, // ikk -> Latn
+    {0xCD480000u, 40u}, // ikt -> Latn
+    {0xD9480000u, 40u}, // ikw -> Latn
+    {0xDD480000u, 40u}, // ikx -> Latn
+    {0xB9680000u, 40u}, // ilo -> Latn
+    {0xB9880000u, 40u}, // imo -> Latn
+    {0x696E0000u, 40u}, // in -> Latn
     {0x9DA80000u, 15u}, // inh -> Cyrl
-    {0x69730000u, 41u}, // is -> Latn
-    {0x69740000u, 41u}, // it -> Latn
+    {0xD1C80000u, 40u}, // iou -> Latn
+    {0xA2280000u, 40u}, // iri -> Latn
+    {0x69730000u, 40u}, // is -> Latn
+    {0x69740000u, 40u}, // it -> Latn
     {0x69750000u,  9u}, // iu -> Cans
     {0x69770000u, 27u}, // iw -> Hebr
-    {0x9F280000u, 41u}, // izh -> Latn
+    {0xB2C80000u, 40u}, // iwm -> Latn
+    {0xCAC80000u, 40u}, // iws -> Latn
+    {0x9F280000u, 40u}, // izh -> Latn
+    {0xA3280000u, 40u}, // izi -> Latn
     {0x6A610000u, 31u}, // ja -> Jpan
-    {0xB0090000u, 41u}, // jam -> Latn
-    {0xB8C90000u, 41u}, // jgo -> Latn
+    {0x84090000u, 40u}, // jab -> Latn
+    {0xB0090000u, 40u}, // jam -> Latn
+    {0xD0290000u, 40u}, // jbu -> Latn
+    {0xB4890000u, 40u}, // jen -> Latn
+    {0xA8C90000u, 40u}, // jgk -> Latn
+    {0xB8C90000u, 40u}, // jgo -> Latn
     {0x6A690000u, 27u}, // ji -> Hebr
-    {0x89890000u, 41u}, // jmc -> Latn
+    {0x85090000u, 40u}, // jib -> Latn
+    {0x89890000u, 40u}, // jmc -> Latn
     {0xAD890000u, 16u}, // jml -> Deva
-    {0xCE890000u, 41u}, // jut -> Latn
-    {0x6A760000u, 41u}, // jv -> Latn
-    {0x6A770000u, 41u}, // jw -> Latn
+    {0x82290000u, 40u}, // jra -> Latn
+    {0xCE890000u, 40u}, // jut -> Latn
+    {0x6A760000u, 40u}, // jv -> Latn
+    {0x6A770000u, 40u}, // jw -> Latn
     {0x6B610000u, 19u}, // ka -> Geor
     {0x800A0000u, 15u}, // kaa -> Cyrl
-    {0x840A0000u, 41u}, // kab -> Latn
-    {0x880A0000u, 41u}, // kac -> Latn
-    {0xA40A0000u, 41u}, // kaj -> Latn
-    {0xB00A0000u, 41u}, // kam -> Latn
-    {0xB80A0000u, 41u}, // kao -> Latn
+    {0x840A0000u, 40u}, // kab -> Latn
+    {0x880A0000u, 40u}, // kac -> Latn
+    {0x8C0A0000u, 40u}, // kad -> Latn
+    {0xA00A0000u, 40u}, // kai -> Latn
+    {0xA40A0000u, 40u}, // kaj -> Latn
+    {0xB00A0000u, 40u}, // kam -> Latn
+    {0xB80A0000u, 40u}, // kao -> Latn
     {0x8C2A0000u, 15u}, // kbd -> Cyrl
-    {0x984A0000u, 41u}, // kcg -> Latn
-    {0xA84A0000u, 41u}, // kck -> Latn
-    {0x906A0000u, 41u}, // kde -> Latn
-    {0xCC6A0000u, 79u}, // kdt -> Thai
-    {0x808A0000u, 41u}, // kea -> Latn
-    {0xB48A0000u, 41u}, // ken -> Latn
-    {0xB8AA0000u, 41u}, // kfo -> Latn
+    {0xB02A0000u, 40u}, // kbm -> Latn
+    {0xBC2A0000u, 40u}, // kbp -> Latn
+    {0xC02A0000u, 40u}, // kbq -> Latn
+    {0xDC2A0000u, 40u}, // kbx -> Latn
+    {0xE02A0000u,  1u}, // kby -> Arab
+    {0x984A0000u, 40u}, // kcg -> Latn
+    {0xA84A0000u, 40u}, // kck -> Latn
+    {0xAC4A0000u, 40u}, // kcl -> Latn
+    {0xCC4A0000u, 40u}, // kct -> Latn
+    {0x906A0000u, 40u}, // kde -> Latn
+    {0x9C6A0000u,  1u}, // kdh -> Arab
+    {0xAC6A0000u, 40u}, // kdl -> Latn
+    {0xCC6A0000u, 80u}, // kdt -> Thai
+    {0x808A0000u, 40u}, // kea -> Latn
+    {0xB48A0000u, 40u}, // ken -> Latn
+    {0xE48A0000u, 40u}, // kez -> Latn
+    {0xB8AA0000u, 40u}, // kfo -> Latn
     {0xC4AA0000u, 16u}, // kfr -> Deva
     {0xE0AA0000u, 16u}, // kfy -> Deva
-    {0x6B670000u, 41u}, // kg -> Latn
-    {0x90CA0000u, 41u}, // kge -> Latn
-    {0xBCCA0000u, 41u}, // kgp -> Latn
-    {0x80EA0000u, 41u}, // kha -> Latn
+    {0x6B670000u, 40u}, // kg -> Latn
+    {0x90CA0000u, 40u}, // kge -> Latn
+    {0x94CA0000u, 40u}, // kgf -> Latn
+    {0xBCCA0000u, 40u}, // kgp -> Latn
+    {0x80EA0000u, 40u}, // kha -> Latn
     {0x84EA0000u, 73u}, // khb -> Talu
     {0xB4EA0000u, 16u}, // khn -> Deva
-    {0xC0EA0000u, 41u}, // khq -> Latn
-    {0xCCEA0000u, 53u}, // kht -> Mymr
+    {0xC0EA0000u, 40u}, // khq -> Latn
+    {0xC8EA0000u, 40u}, // khs -> Latn
+    {0xCCEA0000u, 52u}, // kht -> Mymr
     {0xD8EA0000u,  1u}, // khw -> Arab
-    {0x6B690000u, 41u}, // ki -> Latn
-    {0xD10A0000u, 41u}, // kiu -> Latn
-    {0x6B6A0000u, 41u}, // kj -> Latn
-    {0x992A0000u, 40u}, // kjg -> Laoo
+    {0xE4EA0000u, 40u}, // khz -> Latn
+    {0x6B690000u, 40u}, // ki -> Latn
+    {0xA50A0000u, 40u}, // kij -> Latn
+    {0xD10A0000u, 40u}, // kiu -> Latn
+    {0xD90A0000u, 40u}, // kiw -> Latn
+    {0x6B6A0000u, 40u}, // kj -> Latn
+    {0x8D2A0000u, 40u}, // kjd -> Latn
+    {0x992A0000u, 39u}, // kjg -> Laoo
+    {0xC92A0000u, 40u}, // kjs -> Latn
+    {0xE12A0000u, 40u}, // kjy -> Latn
     {0x6B6B0000u, 15u}, // kk -> Cyrl
     {0x6B6B4146u,  1u}, // kk-AF -> Arab
     {0x6B6B434Eu,  1u}, // kk-CN -> Arab
     {0x6B6B4952u,  1u}, // kk-IR -> Arab
     {0x6B6B4D4Eu,  1u}, // kk-MN -> Arab
-    {0xA54A0000u, 41u}, // kkj -> Latn
-    {0x6B6C0000u, 41u}, // kl -> Latn
-    {0xB56A0000u, 41u}, // kln -> Latn
+    {0x894A0000u, 40u}, // kkc -> Latn
+    {0xA54A0000u, 40u}, // kkj -> Latn
+    {0x6B6C0000u, 40u}, // kl -> Latn
+    {0xB56A0000u, 40u}, // kln -> Latn
+    {0xC16A0000u, 40u}, // klq -> Latn
+    {0xCD6A0000u, 40u}, // klt -> Latn
+    {0xDD6A0000u, 40u}, // klx -> Latn
     {0x6B6D0000u, 35u}, // km -> Khmr
-    {0x858A0000u, 41u}, // kmb -> Latn
+    {0x858A0000u, 40u}, // kmb -> Latn
+    {0x9D8A0000u, 40u}, // kmh -> Latn
+    {0xB98A0000u, 40u}, // kmo -> Latn
+    {0xC98A0000u, 40u}, // kms -> Latn
+    {0xD18A0000u, 40u}, // kmu -> Latn
+    {0xD98A0000u, 40u}, // kmw -> Latn
     {0x6B6E0000u, 36u}, // kn -> Knda
+    {0xBDAA0000u, 40u}, // knp -> Latn
     {0x6B6F0000u, 37u}, // ko -> Kore
     {0xA1CA0000u, 15u}, // koi -> Cyrl
     {0xA9CA0000u, 16u}, // kok -> Deva
-    {0xC9CA0000u, 41u}, // kos -> Latn
-    {0x91EA0000u, 41u}, // kpe -> Latn
+    {0xADCA0000u, 40u}, // kol -> Latn
+    {0xC9CA0000u, 40u}, // kos -> Latn
+    {0xE5CA0000u, 40u}, // koz -> Latn
+    {0x91EA0000u, 40u}, // kpe -> Latn
+    {0x95EA0000u, 40u}, // kpf -> Latn
+    {0xB9EA0000u, 40u}, // kpo -> Latn
+    {0xC5EA0000u, 40u}, // kpr -> Latn
+    {0xDDEA0000u, 40u}, // kpx -> Latn
+    {0x860A0000u, 40u}, // kqb -> Latn
+    {0x960A0000u, 40u}, // kqf -> Latn
+    {0xCA0A0000u, 40u}, // kqs -> Latn
+    {0xE20A0000u, 18u}, // kqy -> Ethi
     {0x8A2A0000u, 15u}, // krc -> Cyrl
-    {0xA22A0000u, 41u}, // kri -> Latn
-    {0xA62A0000u, 41u}, // krj -> Latn
-    {0xAE2A0000u, 41u}, // krl -> Latn
+    {0xA22A0000u, 40u}, // kri -> Latn
+    {0xA62A0000u, 40u}, // krj -> Latn
+    {0xAE2A0000u, 40u}, // krl -> Latn
+    {0xCA2A0000u, 40u}, // krs -> Latn
     {0xD22A0000u, 16u}, // kru -> Deva
     {0x6B730000u,  1u}, // ks -> Arab
-    {0x864A0000u, 41u}, // ksb -> Latn
-    {0x964A0000u, 41u}, // ksf -> Latn
-    {0x9E4A0000u, 41u}, // ksh -> Latn
-    {0x6B750000u, 41u}, // ku -> Latn
+    {0x864A0000u, 40u}, // ksb -> Latn
+    {0x8E4A0000u, 40u}, // ksd -> Latn
+    {0x964A0000u, 40u}, // ksf -> Latn
+    {0x9E4A0000u, 40u}, // ksh -> Latn
+    {0xA64A0000u, 40u}, // ksj -> Latn
+    {0xC64A0000u, 40u}, // ksr -> Latn
+    {0x866A0000u, 18u}, // ktb -> Ethi
+    {0xB26A0000u, 40u}, // ktm -> Latn
+    {0xBA6A0000u, 40u}, // kto -> Latn
+    {0x6B750000u, 40u}, // ku -> Latn
     {0x6B754952u,  1u}, // ku-IR -> Arab
     {0x6B754C42u,  1u}, // ku-LB -> Arab
+    {0x868A0000u, 40u}, // kub -> Latn
+    {0x8E8A0000u, 40u}, // kud -> Latn
+    {0x928A0000u, 40u}, // kue -> Latn
+    {0xA68A0000u, 40u}, // kuj -> Latn
     {0xB28A0000u, 15u}, // kum -> Cyrl
+    {0xB68A0000u, 40u}, // kun -> Latn
+    {0xBE8A0000u, 40u}, // kup -> Latn
+    {0xCA8A0000u, 40u}, // kus -> Latn
     {0x6B760000u, 15u}, // kv -> Cyrl
-    {0xC6AA0000u, 41u}, // kvr -> Latn
+    {0x9AAA0000u, 40u}, // kvg -> Latn
+    {0xC6AA0000u, 40u}, // kvr -> Latn
     {0xDEAA0000u,  1u}, // kvx -> Arab
-    {0x6B770000u, 41u}, // kw -> Latn
-    {0xB2EA0000u, 79u}, // kxm -> Thai
+    {0x6B770000u, 40u}, // kw -> Latn
+    {0xA6CA0000u, 40u}, // kwj -> Latn
+    {0xBACA0000u, 40u}, // kwo -> Latn
+    {0x82EA0000u, 40u}, // kxa -> Latn
+    {0x8AEA0000u, 18u}, // kxc -> Ethi
+    {0xB2EA0000u, 80u}, // kxm -> Thai
     {0xBEEA0000u,  1u}, // kxp -> Arab
+    {0xDAEA0000u, 40u}, // kxw -> Latn
+    {0xE6EA0000u, 40u}, // kxz -> Latn
     {0x6B790000u, 15u}, // ky -> Cyrl
     {0x6B79434Eu,  1u}, // ky-CN -> Arab
-    {0x6B795452u, 41u}, // ky-TR -> Latn
-    {0x6C610000u, 41u}, // la -> Latn
-    {0x840B0000u, 43u}, // lab -> Lina
+    {0x6B795452u, 40u}, // ky-TR -> Latn
+    {0x930A0000u, 40u}, // kye -> Latn
+    {0xDF0A0000u, 40u}, // kyx -> Latn
+    {0xC72A0000u, 40u}, // kzr -> Latn
+    {0x6C610000u, 40u}, // la -> Latn
+    {0x840B0000u, 42u}, // lab -> Lina
     {0x8C0B0000u, 27u}, // lad -> Hebr
-    {0x980B0000u, 41u}, // lag -> Latn
+    {0x980B0000u, 40u}, // lag -> Latn
     {0x9C0B0000u,  1u}, // lah -> Arab
-    {0xA40B0000u, 41u}, // laj -> Latn
-    {0x6C620000u, 41u}, // lb -> Latn
+    {0xA40B0000u, 40u}, // laj -> Latn
+    {0xC80B0000u, 40u}, // las -> Latn
+    {0x6C620000u, 40u}, // lb -> Latn
     {0x902B0000u, 15u}, // lbe -> Cyrl
-    {0xD82B0000u, 41u}, // lbw -> Latn
-    {0xBC4B0000u, 79u}, // lcp -> Thai
-    {0xBC8B0000u, 42u}, // lep -> Lepc
+    {0xD02B0000u, 40u}, // lbu -> Latn
+    {0xD82B0000u, 40u}, // lbw -> Latn
+    {0xB04B0000u, 40u}, // lcm -> Latn
+    {0xBC4B0000u, 80u}, // lcp -> Thai
+    {0x846B0000u, 40u}, // ldb -> Latn
+    {0x8C8B0000u, 40u}, // led -> Latn
+    {0x908B0000u, 40u}, // lee -> Latn
+    {0xB08B0000u, 40u}, // lem -> Latn
+    {0xBC8B0000u, 41u}, // lep -> Lepc
+    {0xC08B0000u, 40u}, // leq -> Latn
+    {0xD08B0000u, 40u}, // leu -> Latn
     {0xE48B0000u, 15u}, // lez -> Cyrl
-    {0x6C670000u, 41u}, // lg -> Latn
-    {0x6C690000u, 41u}, // li -> Latn
+    {0x6C670000u, 40u}, // lg -> Latn
+    {0x98CB0000u, 40u}, // lgg -> Latn
+    {0x6C690000u, 40u}, // li -> Latn
+    {0x810B0000u, 40u}, // lia -> Latn
+    {0x8D0B0000u, 40u}, // lid -> Latn
     {0x950B0000u, 16u}, // lif -> Deva
-    {0xA50B0000u, 41u}, // lij -> Latn
-    {0xC90B0000u, 44u}, // lis -> Lisu
-    {0xBD2B0000u, 41u}, // ljp -> Latn
+    {0x990B0000u, 40u}, // lig -> Latn
+    {0x9D0B0000u, 40u}, // lih -> Latn
+    {0xA50B0000u, 40u}, // lij -> Latn
+    {0xC90B0000u, 43u}, // lis -> Lisu
+    {0xBD2B0000u, 40u}, // ljp -> Latn
     {0xA14B0000u,  1u}, // lki -> Arab
-    {0xCD4B0000u, 41u}, // lkt -> Latn
-    {0xB58B0000u, 76u}, // lmn -> Telu
-    {0xB98B0000u, 41u}, // lmo -> Latn
-    {0x6C6E0000u, 41u}, // ln -> Latn
-    {0x6C6F0000u, 40u}, // lo -> Laoo
-    {0xADCB0000u, 41u}, // lol -> Latn
-    {0xE5CB0000u, 41u}, // loz -> Latn
+    {0xCD4B0000u, 40u}, // lkt -> Latn
+    {0x916B0000u, 40u}, // lle -> Latn
+    {0xB56B0000u, 40u}, // lln -> Latn
+    {0xB58B0000u, 77u}, // lmn -> Telu
+    {0xB98B0000u, 40u}, // lmo -> Latn
+    {0xBD8B0000u, 40u}, // lmp -> Latn
+    {0x6C6E0000u, 40u}, // ln -> Latn
+    {0xC9AB0000u, 40u}, // lns -> Latn
+    {0xD1AB0000u, 40u}, // lnu -> Latn
+    {0x6C6F0000u, 39u}, // lo -> Laoo
+    {0xA5CB0000u, 40u}, // loj -> Latn
+    {0xA9CB0000u, 40u}, // lok -> Latn
+    {0xADCB0000u, 40u}, // lol -> Latn
+    {0xC5CB0000u, 40u}, // lor -> Latn
+    {0xC9CB0000u, 40u}, // los -> Latn
+    {0xE5CB0000u, 40u}, // loz -> Latn
     {0x8A2B0000u,  1u}, // lrc -> Arab
-    {0x6C740000u, 41u}, // lt -> Latn
-    {0x9A6B0000u, 41u}, // ltg -> Latn
-    {0x6C750000u, 41u}, // lu -> Latn
-    {0x828B0000u, 41u}, // lua -> Latn
-    {0xBA8B0000u, 41u}, // luo -> Latn
-    {0xE28B0000u, 41u}, // luy -> Latn
+    {0x6C740000u, 40u}, // lt -> Latn
+    {0x9A6B0000u, 40u}, // ltg -> Latn
+    {0x6C750000u, 40u}, // lu -> Latn
+    {0x828B0000u, 40u}, // lua -> Latn
+    {0xBA8B0000u, 40u}, // luo -> Latn
+    {0xE28B0000u, 40u}, // luy -> Latn
     {0xE68B0000u,  1u}, // luz -> Arab
-    {0x6C760000u, 41u}, // lv -> Latn
-    {0xAECB0000u, 79u}, // lwl -> Thai
+    {0x6C760000u, 40u}, // lv -> Latn
+    {0xAECB0000u, 80u}, // lwl -> Thai
     {0x9F2B0000u, 24u}, // lzh -> Hans
-    {0xE72B0000u, 41u}, // lzz -> Latn
-    {0x8C0C0000u, 41u}, // mad -> Latn
-    {0x940C0000u, 41u}, // maf -> Latn
+    {0xE72B0000u, 40u}, // lzz -> Latn
+    {0x8C0C0000u, 40u}, // mad -> Latn
+    {0x940C0000u, 40u}, // maf -> Latn
     {0x980C0000u, 16u}, // mag -> Deva
     {0xA00C0000u, 16u}, // mai -> Deva
-    {0xA80C0000u, 41u}, // mak -> Latn
-    {0xB40C0000u, 41u}, // man -> Latn
-    {0xB40C474Eu, 55u}, // man-GN -> Nkoo
-    {0xC80C0000u, 41u}, // mas -> Latn
-    {0xE40C0000u, 41u}, // maz -> Latn
+    {0xA80C0000u, 40u}, // mak -> Latn
+    {0xB40C0000u, 40u}, // man -> Latn
+    {0xB40C474Eu, 54u}, // man-GN -> Nkoo
+    {0xC80C0000u, 40u}, // mas -> Latn
+    {0xD80C0000u, 40u}, // maw -> Latn
+    {0xE40C0000u, 40u}, // maz -> Latn
+    {0x9C2C0000u, 40u}, // mbh -> Latn
+    {0xB82C0000u, 40u}, // mbo -> Latn
+    {0xC02C0000u, 40u}, // mbq -> Latn
+    {0xD02C0000u, 40u}, // mbu -> Latn
+    {0xD82C0000u, 40u}, // mbw -> Latn
+    {0xA04C0000u, 40u}, // mci -> Latn
+    {0xBC4C0000u, 40u}, // mcp -> Latn
+    {0xC04C0000u, 40u}, // mcq -> Latn
+    {0xC44C0000u, 40u}, // mcr -> Latn
+    {0xD04C0000u, 40u}, // mcu -> Latn
+    {0x806C0000u, 40u}, // mda -> Latn
+    {0x906C0000u,  1u}, // mde -> Arab
     {0x946C0000u, 15u}, // mdf -> Cyrl
-    {0x9C6C0000u, 41u}, // mdh -> Latn
-    {0xC46C0000u, 41u}, // mdr -> Latn
-    {0xB48C0000u, 41u}, // men -> Latn
-    {0xC48C0000u, 41u}, // mer -> Latn
+    {0x9C6C0000u, 40u}, // mdh -> Latn
+    {0xA46C0000u, 40u}, // mdj -> Latn
+    {0xC46C0000u, 40u}, // mdr -> Latn
+    {0xDC6C0000u, 18u}, // mdx -> Ethi
+    {0x8C8C0000u, 40u}, // med -> Latn
+    {0x908C0000u, 40u}, // mee -> Latn
+    {0xA88C0000u, 40u}, // mek -> Latn
+    {0xB48C0000u, 40u}, // men -> Latn
+    {0xC48C0000u, 40u}, // mer -> Latn
+    {0xCC8C0000u, 40u}, // met -> Latn
+    {0xD08C0000u, 40u}, // meu -> Latn
     {0x80AC0000u,  1u}, // mfa -> Arab
-    {0x90AC0000u, 41u}, // mfe -> Latn
-    {0x6D670000u, 41u}, // mg -> Latn
-    {0x9CCC0000u, 41u}, // mgh -> Latn
-    {0xB8CC0000u, 41u}, // mgo -> Latn
+    {0x90AC0000u, 40u}, // mfe -> Latn
+    {0xB4AC0000u, 40u}, // mfn -> Latn
+    {0xB8AC0000u, 40u}, // mfo -> Latn
+    {0xC0AC0000u, 40u}, // mfq -> Latn
+    {0x6D670000u, 40u}, // mg -> Latn
+    {0x9CCC0000u, 40u}, // mgh -> Latn
+    {0xACCC0000u, 40u}, // mgl -> Latn
+    {0xB8CC0000u, 40u}, // mgo -> Latn
     {0xBCCC0000u, 16u}, // mgp -> Deva
-    {0xE0CC0000u, 41u}, // mgy -> Latn
-    {0x6D680000u, 41u}, // mh -> Latn
-    {0x6D690000u, 41u}, // mi -> Latn
-    {0xB50C0000u, 41u}, // min -> Latn
+    {0xE0CC0000u, 40u}, // mgy -> Latn
+    {0x6D680000u, 40u}, // mh -> Latn
+    {0xA0EC0000u, 40u}, // mhi -> Latn
+    {0xACEC0000u, 40u}, // mhl -> Latn
+    {0x6D690000u, 40u}, // mi -> Latn
+    {0x950C0000u, 40u}, // mif -> Latn
+    {0xB50C0000u, 40u}, // min -> Latn
     {0xC90C0000u, 26u}, // mis -> Hatr
+    {0xD90C0000u, 40u}, // miw -> Latn
     {0x6D6B0000u, 15u}, // mk -> Cyrl
-    {0x6D6C0000u, 50u}, // ml -> Mlym
-    {0xC96C0000u, 41u}, // mls -> Latn
+    {0xA14C0000u,  1u}, // mki -> Arab
+    {0xAD4C0000u, 40u}, // mkl -> Latn
+    {0xBD4C0000u, 40u}, // mkp -> Latn
+    {0xD94C0000u, 40u}, // mkw -> Latn
+    {0x6D6C0000u, 49u}, // ml -> Mlym
+    {0x916C0000u, 40u}, // mle -> Latn
+    {0xBD6C0000u, 40u}, // mlp -> Latn
+    {0xC96C0000u, 40u}, // mls -> Latn
+    {0xB98C0000u, 40u}, // mmo -> Latn
+    {0xD18C0000u, 40u}, // mmu -> Latn
+    {0xDD8C0000u, 40u}, // mmx -> Latn
     {0x6D6E0000u, 15u}, // mn -> Cyrl
-    {0x6D6E434Eu, 51u}, // mn-CN -> Mong
+    {0x6D6E434Eu, 50u}, // mn-CN -> Mong
+    {0x81AC0000u, 40u}, // mna -> Latn
+    {0x95AC0000u, 40u}, // mnf -> Latn
     {0xA1AC0000u,  7u}, // mni -> Beng
-    {0xD9AC0000u, 53u}, // mnw -> Mymr
-    {0x91CC0000u, 41u}, // moe -> Latn
-    {0x9DCC0000u, 41u}, // moh -> Latn
-    {0xC9CC0000u, 41u}, // mos -> Latn
+    {0xD9AC0000u, 52u}, // mnw -> Mymr
+    {0x81CC0000u, 40u}, // moa -> Latn
+    {0x91CC0000u, 40u}, // moe -> Latn
+    {0x9DCC0000u, 40u}, // moh -> Latn
+    {0xC9CC0000u, 40u}, // mos -> Latn
+    {0xDDCC0000u, 40u}, // mox -> Latn
+    {0xBDEC0000u, 40u}, // mpp -> Latn
+    {0xC9EC0000u, 40u}, // mps -> Latn
+    {0xCDEC0000u, 40u}, // mpt -> Latn
+    {0xDDEC0000u, 40u}, // mpx -> Latn
+    {0xAE0C0000u, 40u}, // mql -> Latn
     {0x6D720000u, 16u}, // mr -> Deva
     {0x8E2C0000u, 16u}, // mrd -> Deva
     {0xA62C0000u, 15u}, // mrj -> Cyrl
-    {0xD22C0000u, 52u}, // mru -> Mroo
-    {0x6D730000u, 41u}, // ms -> Latn
+    {0xBA2C0000u, 51u}, // mro -> Mroo
+    {0x6D730000u, 40u}, // ms -> Latn
     {0x6D734343u,  1u}, // ms-CC -> Arab
     {0x6D734944u,  1u}, // ms-ID -> Arab
-    {0x6D740000u, 41u}, // mt -> Latn
+    {0x6D740000u, 40u}, // mt -> Latn
+    {0x8A6C0000u, 40u}, // mtc -> Latn
+    {0x966C0000u, 40u}, // mtf -> Latn
+    {0xA26C0000u, 40u}, // mti -> Latn
     {0xC66C0000u, 16u}, // mtr -> Deva
-    {0x828C0000u, 41u}, // mua -> Latn
-    {0xCA8C0000u, 41u}, // mus -> Latn
+    {0x828C0000u, 40u}, // mua -> Latn
+    {0xC68C0000u, 40u}, // mur -> Latn
+    {0xCA8C0000u, 40u}, // mus -> Latn
+    {0x82AC0000u, 40u}, // mva -> Latn
+    {0xB6AC0000u, 40u}, // mvn -> Latn
     {0xE2AC0000u,  1u}, // mvy -> Arab
-    {0xAACC0000u, 41u}, // mwk -> Latn
+    {0xAACC0000u, 40u}, // mwk -> Latn
     {0xC6CC0000u, 16u}, // mwr -> Deva
-    {0xD6CC0000u, 41u}, // mwv -> Latn
-    {0x8AEC0000u, 41u}, // mxc -> Latn
-    {0x6D790000u, 53u}, // my -> Mymr
+    {0xD6CC0000u, 40u}, // mwv -> Latn
+    {0x8AEC0000u, 40u}, // mxc -> Latn
+    {0xB2EC0000u, 40u}, // mxm -> Latn
+    {0x6D790000u, 52u}, // my -> Mymr
+    {0xAB0C0000u, 40u}, // myk -> Latn
+    {0xB30C0000u, 18u}, // mym -> Ethi
     {0xD70C0000u, 15u}, // myv -> Cyrl
-    {0xDF0C0000u, 41u}, // myx -> Latn
-    {0xE70C0000u, 47u}, // myz -> Mand
+    {0xDB0C0000u, 40u}, // myw -> Latn
+    {0xDF0C0000u, 40u}, // myx -> Latn
+    {0xE70C0000u, 46u}, // myz -> Mand
+    {0xAB2C0000u, 40u}, // mzk -> Latn
+    {0xB32C0000u, 40u}, // mzm -> Latn
     {0xB72C0000u,  1u}, // mzn -> Arab
-    {0x6E610000u, 41u}, // na -> Latn
+    {0xBF2C0000u, 40u}, // mzp -> Latn
+    {0xDB2C0000u, 40u}, // mzw -> Latn
+    {0xE72C0000u, 40u}, // mzz -> Latn
+    {0x6E610000u, 40u}, // na -> Latn
+    {0x880D0000u, 40u}, // nac -> Latn
+    {0x940D0000u, 40u}, // naf -> Latn
+    {0xA80D0000u, 40u}, // nak -> Latn
     {0xB40D0000u, 24u}, // nan -> Hans
-    {0xBC0D0000u, 41u}, // nap -> Latn
-    {0xC00D0000u, 41u}, // naq -> Latn
-    {0x6E620000u, 41u}, // nb -> Latn
-    {0x9C4D0000u, 41u}, // nch -> Latn
-    {0x6E640000u, 41u}, // nd -> Latn
-    {0x886D0000u, 41u}, // ndc -> Latn
-    {0xC86D0000u, 41u}, // nds -> Latn
+    {0xBC0D0000u, 40u}, // nap -> Latn
+    {0xC00D0000u, 40u}, // naq -> Latn
+    {0xC80D0000u, 40u}, // nas -> Latn
+    {0x6E620000u, 40u}, // nb -> Latn
+    {0x804D0000u, 40u}, // nca -> Latn
+    {0x904D0000u, 40u}, // nce -> Latn
+    {0x944D0000u, 40u}, // ncf -> Latn
+    {0x9C4D0000u, 40u}, // nch -> Latn
+    {0xB84D0000u, 40u}, // nco -> Latn
+    {0xD04D0000u, 40u}, // ncu -> Latn
+    {0x6E640000u, 40u}, // nd -> Latn
+    {0x886D0000u, 40u}, // ndc -> Latn
+    {0xC86D0000u, 40u}, // nds -> Latn
     {0x6E650000u, 16u}, // ne -> Deva
+    {0x848D0000u, 40u}, // neb -> Latn
     {0xD88D0000u, 16u}, // new -> Deva
-    {0x6E670000u, 41u}, // ng -> Latn
-    {0xACCD0000u, 41u}, // ngl -> Latn
-    {0x90ED0000u, 41u}, // nhe -> Latn
-    {0xD8ED0000u, 41u}, // nhw -> Latn
-    {0xA50D0000u, 41u}, // nij -> Latn
-    {0xD10D0000u, 41u}, // niu -> Latn
-    {0xB92D0000u, 41u}, // njo -> Latn
-    {0x6E6C0000u, 41u}, // nl -> Latn
-    {0x998D0000u, 41u}, // nmg -> Latn
-    {0x6E6E0000u, 41u}, // nn -> Latn
-    {0x9DAD0000u, 41u}, // nnh -> Latn
-    {0x6E6F0000u, 41u}, // no -> Latn
-    {0x8DCD0000u, 39u}, // nod -> Lana
+    {0xDC8D0000u, 40u}, // nex -> Latn
+    {0xC4AD0000u, 40u}, // nfr -> Latn
+    {0x6E670000u, 40u}, // ng -> Latn
+    {0x80CD0000u, 40u}, // nga -> Latn
+    {0x84CD0000u, 40u}, // ngb -> Latn
+    {0xACCD0000u, 40u}, // ngl -> Latn
+    {0x84ED0000u, 40u}, // nhb -> Latn
+    {0x90ED0000u, 40u}, // nhe -> Latn
+    {0xD8ED0000u, 40u}, // nhw -> Latn
+    {0x950D0000u, 40u}, // nif -> Latn
+    {0xA10D0000u, 40u}, // nii -> Latn
+    {0xA50D0000u, 40u}, // nij -> Latn
+    {0xB50D0000u, 40u}, // nin -> Latn
+    {0xD10D0000u, 40u}, // niu -> Latn
+    {0xE10D0000u, 40u}, // niy -> Latn
+    {0xE50D0000u, 40u}, // niz -> Latn
+    {0xB92D0000u, 40u}, // njo -> Latn
+    {0x994D0000u, 40u}, // nkg -> Latn
+    {0xB94D0000u, 40u}, // nko -> Latn
+    {0x6E6C0000u, 40u}, // nl -> Latn
+    {0x998D0000u, 40u}, // nmg -> Latn
+    {0xE58D0000u, 40u}, // nmz -> Latn
+    {0x6E6E0000u, 40u}, // nn -> Latn
+    {0x95AD0000u, 40u}, // nnf -> Latn
+    {0x9DAD0000u, 40u}, // nnh -> Latn
+    {0xA9AD0000u, 40u}, // nnk -> Latn
+    {0xB1AD0000u, 40u}, // nnm -> Latn
+    {0x6E6F0000u, 40u}, // no -> Latn
+    {0x8DCD0000u, 38u}, // nod -> Lana
     {0x91CD0000u, 16u}, // noe -> Deva
     {0xB5CD0000u, 64u}, // non -> Runr
-    {0xBA0D0000u, 55u}, // nqo -> Nkoo
-    {0x6E720000u, 41u}, // nr -> Latn
+    {0xBDCD0000u, 40u}, // nop -> Latn
+    {0xD1CD0000u, 40u}, // nou -> Latn
+    {0xBA0D0000u, 54u}, // nqo -> Nkoo
+    {0x6E720000u, 40u}, // nr -> Latn
+    {0x862D0000u, 40u}, // nrb -> Latn
     {0xAA4D0000u,  9u}, // nsk -> Cans
-    {0xBA4D0000u, 41u}, // nso -> Latn
-    {0xCA8D0000u, 41u}, // nus -> Latn
-    {0x6E760000u, 41u}, // nv -> Latn
-    {0xC2ED0000u, 41u}, // nxq -> Latn
-    {0x6E790000u, 41u}, // ny -> Latn
-    {0xB30D0000u, 41u}, // nym -> Latn
-    {0xB70D0000u, 41u}, // nyn -> Latn
-    {0xA32D0000u, 41u}, // nzi -> Latn
-    {0x6F630000u, 41u}, // oc -> Latn
-    {0x6F6D0000u, 41u}, // om -> Latn
-    {0x6F720000u, 58u}, // or -> Orya
+    {0xB64D0000u, 40u}, // nsn -> Latn
+    {0xBA4D0000u, 40u}, // nso -> Latn
+    {0xCA4D0000u, 40u}, // nss -> Latn
+    {0xB26D0000u, 40u}, // ntm -> Latn
+    {0xC66D0000u, 40u}, // ntr -> Latn
+    {0xA28D0000u, 40u}, // nui -> Latn
+    {0xBE8D0000u, 40u}, // nup -> Latn
+    {0xCA8D0000u, 40u}, // nus -> Latn
+    {0xD68D0000u, 40u}, // nuv -> Latn
+    {0xDE8D0000u, 40u}, // nux -> Latn
+    {0x6E760000u, 40u}, // nv -> Latn
+    {0x86CD0000u, 40u}, // nwb -> Latn
+    {0xC2ED0000u, 40u}, // nxq -> Latn
+    {0xC6ED0000u, 40u}, // nxr -> Latn
+    {0x6E790000u, 40u}, // ny -> Latn
+    {0xB30D0000u, 40u}, // nym -> Latn
+    {0xB70D0000u, 40u}, // nyn -> Latn
+    {0xA32D0000u, 40u}, // nzi -> Latn
+    {0x6F630000u, 40u}, // oc -> Latn
+    {0x88CE0000u, 40u}, // ogc -> Latn
+    {0xC54E0000u, 40u}, // okr -> Latn
+    {0xD54E0000u, 40u}, // okv -> Latn
+    {0x6F6D0000u, 40u}, // om -> Latn
+    {0x99AE0000u, 40u}, // ong -> Latn
+    {0xB5AE0000u, 40u}, // onn -> Latn
+    {0xC9AE0000u, 40u}, // ons -> Latn
+    {0xB1EE0000u, 40u}, // opm -> Latn
+    {0x6F720000u, 57u}, // or -> Orya
+    {0xBA2E0000u, 40u}, // oro -> Latn
+    {0xD22E0000u,  1u}, // oru -> Arab
     {0x6F730000u, 15u}, // os -> Cyrl
-    {0xAA6E0000u, 57u}, // otk -> Orkh
+    {0x824E0000u, 58u}, // osa -> Osge
+    {0x826E0000u,  1u}, // ota -> Arab
+    {0xAA6E0000u, 56u}, // otk -> Orkh
+    {0xB32E0000u, 40u}, // ozm -> Latn
     {0x70610000u, 23u}, // pa -> Guru
     {0x7061504Bu,  1u}, // pa-PK -> Arab
-    {0x980F0000u, 41u}, // pag -> Latn
+    {0x980F0000u, 40u}, // pag -> Latn
     {0xAC0F0000u, 60u}, // pal -> Phli
-    {0xB00F0000u, 41u}, // pam -> Latn
-    {0xBC0F0000u, 41u}, // pap -> Latn
-    {0xD00F0000u, 41u}, // pau -> Latn
-    {0x8C4F0000u, 41u}, // pcd -> Latn
-    {0xB04F0000u, 41u}, // pcm -> Latn
-    {0x886F0000u, 41u}, // pdc -> Latn
-    {0xCC6F0000u, 41u}, // pdt -> Latn
-    {0xB88F0000u, 83u}, // peo -> Xpeo
-    {0xACAF0000u, 41u}, // pfl -> Latn
+    {0xB00F0000u, 40u}, // pam -> Latn
+    {0xBC0F0000u, 40u}, // pap -> Latn
+    {0xD00F0000u, 40u}, // pau -> Latn
+    {0xA02F0000u, 40u}, // pbi -> Latn
+    {0x8C4F0000u, 40u}, // pcd -> Latn
+    {0xB04F0000u, 40u}, // pcm -> Latn
+    {0x886F0000u, 40u}, // pdc -> Latn
+    {0xCC6F0000u, 40u}, // pdt -> Latn
+    {0x8C8F0000u, 40u}, // ped -> Latn
+    {0xB88F0000u, 84u}, // peo -> Xpeo
+    {0xDC8F0000u, 40u}, // pex -> Latn
+    {0xACAF0000u, 40u}, // pfl -> Latn
+    {0xACEF0000u,  1u}, // phl -> Arab
     {0xB4EF0000u, 61u}, // phn -> Phnx
+    {0xAD0F0000u, 40u}, // pil -> Latn
+    {0xBD0F0000u, 40u}, // pip -> Latn
     {0x814F0000u,  8u}, // pka -> Brah
-    {0xB94F0000u, 41u}, // pko -> Latn
-    {0x706C0000u, 41u}, // pl -> Latn
-    {0xC98F0000u, 41u}, // pms -> Latn
+    {0xB94F0000u, 40u}, // pko -> Latn
+    {0x706C0000u, 40u}, // pl -> Latn
+    {0x816F0000u, 40u}, // pla -> Latn
+    {0xC98F0000u, 40u}, // pms -> Latn
+    {0x99AF0000u, 40u}, // png -> Latn
+    {0xB5AF0000u, 40u}, // pnn -> Latn
     {0xCDAF0000u, 21u}, // pnt -> Grek
-    {0xB5CF0000u, 41u}, // pon -> Latn
+    {0xB5CF0000u, 40u}, // pon -> Latn
+    {0xB9EF0000u, 40u}, // ppo -> Latn
     {0x822F0000u, 34u}, // pra -> Khar
     {0x8E2F0000u,  1u}, // prd -> Arab
-    {0x9A2F0000u, 41u}, // prg -> Latn
+    {0x9A2F0000u, 40u}, // prg -> Latn
     {0x70730000u,  1u}, // ps -> Arab
-    {0x70740000u, 41u}, // pt -> Latn
-    {0xD28F0000u, 41u}, // puu -> Latn
-    {0x71750000u, 41u}, // qu -> Latn
-    {0x8A900000u, 41u}, // quc -> Latn
-    {0x9A900000u, 41u}, // qug -> Latn
+    {0xCA4F0000u, 40u}, // pss -> Latn
+    {0x70740000u, 40u}, // pt -> Latn
+    {0xBE6F0000u, 40u}, // ptp -> Latn
+    {0xD28F0000u, 40u}, // puu -> Latn
+    {0x82CF0000u, 40u}, // pwa -> Latn
+    {0x71750000u, 40u}, // qu -> Latn
+    {0x8A900000u, 40u}, // quc -> Latn
+    {0x9A900000u, 40u}, // qug -> Latn
+    {0xA0110000u, 40u}, // rai -> Latn
     {0xA4110000u, 16u}, // raj -> Deva
-    {0x94510000u, 41u}, // rcf -> Latn
-    {0xA4910000u, 41u}, // rej -> Latn
-    {0xB4D10000u, 41u}, // rgn -> Latn
-    {0x81110000u, 41u}, // ria -> Latn
-    {0x95110000u, 77u}, // rif -> Tfng
-    {0x95114E4Cu, 41u}, // rif-NL -> Latn
+    {0xB8110000u, 40u}, // rao -> Latn
+    {0x94510000u, 40u}, // rcf -> Latn
+    {0xA4910000u, 40u}, // rej -> Latn
+    {0xAC910000u, 40u}, // rel -> Latn
+    {0xC8910000u, 40u}, // res -> Latn
+    {0xB4D10000u, 40u}, // rgn -> Latn
+    {0x98F10000u,  1u}, // rhg -> Arab
+    {0x81110000u, 40u}, // ria -> Latn
+    {0x95110000u, 78u}, // rif -> Tfng
+    {0x95114E4Cu, 40u}, // rif-NL -> Latn
     {0xC9310000u, 16u}, // rjs -> Deva
     {0xCD510000u,  7u}, // rkt -> Beng
-    {0x726D0000u, 41u}, // rm -> Latn
-    {0x95910000u, 41u}, // rmf -> Latn
-    {0xB9910000u, 41u}, // rmo -> Latn
+    {0x726D0000u, 40u}, // rm -> Latn
+    {0x95910000u, 40u}, // rmf -> Latn
+    {0xB9910000u, 40u}, // rmo -> Latn
     {0xCD910000u,  1u}, // rmt -> Arab
-    {0xD1910000u, 41u}, // rmu -> Latn
-    {0x726E0000u, 41u}, // rn -> Latn
-    {0x99B10000u, 41u}, // rng -> Latn
-    {0x726F0000u, 41u}, // ro -> Latn
-    {0x85D10000u, 41u}, // rob -> Latn
-    {0x95D10000u, 41u}, // rof -> Latn
-    {0xB2710000u, 41u}, // rtm -> Latn
+    {0xD1910000u, 40u}, // rmu -> Latn
+    {0x726E0000u, 40u}, // rn -> Latn
+    {0x81B10000u, 40u}, // rna -> Latn
+    {0x99B10000u, 40u}, // rng -> Latn
+    {0x726F0000u, 40u}, // ro -> Latn
+    {0x85D10000u, 40u}, // rob -> Latn
+    {0x95D10000u, 40u}, // rof -> Latn
+    {0xB9D10000u, 40u}, // roo -> Latn
+    {0xBA310000u, 40u}, // rro -> Latn
+    {0xB2710000u, 40u}, // rtm -> Latn
     {0x72750000u, 15u}, // ru -> Cyrl
     {0x92910000u, 15u}, // rue -> Cyrl
-    {0x9A910000u, 41u}, // rug -> Latn
-    {0x72770000u, 41u}, // rw -> Latn
-    {0xAAD10000u, 41u}, // rwk -> Latn
+    {0x9A910000u, 40u}, // rug -> Latn
+    {0x72770000u, 40u}, // rw -> Latn
+    {0xAAD10000u, 40u}, // rwk -> Latn
+    {0xBAD10000u, 40u}, // rwo -> Latn
     {0xD3110000u, 33u}, // ryu -> Kana
     {0x73610000u, 16u}, // sa -> Deva
-    {0x94120000u, 41u}, // saf -> Latn
+    {0x94120000u, 40u}, // saf -> Latn
     {0x9C120000u, 15u}, // sah -> Cyrl
-    {0xC0120000u, 41u}, // saq -> Latn
-    {0xC8120000u, 41u}, // sas -> Latn
-    {0xCC120000u, 41u}, // sat -> Latn
+    {0xC0120000u, 40u}, // saq -> Latn
+    {0xC8120000u, 40u}, // sas -> Latn
+    {0xCC120000u, 40u}, // sat -> Latn
     {0xE4120000u, 67u}, // saz -> Saur
-    {0xBC320000u, 41u}, // sbp -> Latn
-    {0x73630000u, 41u}, // sc -> Latn
+    {0x80320000u, 40u}, // sba -> Latn
+    {0x90320000u, 40u}, // sbe -> Latn
+    {0xBC320000u, 40u}, // sbp -> Latn
+    {0x73630000u, 40u}, // sc -> Latn
     {0xA8520000u, 16u}, // sck -> Deva
-    {0xB4520000u, 41u}, // scn -> Latn
-    {0xB8520000u, 41u}, // sco -> Latn
-    {0xC8520000u, 41u}, // scs -> Latn
+    {0xAC520000u,  1u}, // scl -> Arab
+    {0xB4520000u, 40u}, // scn -> Latn
+    {0xB8520000u, 40u}, // sco -> Latn
+    {0xC8520000u, 40u}, // scs -> Latn
     {0x73640000u,  1u}, // sd -> Arab
-    {0x88720000u, 41u}, // sdc -> Latn
+    {0x88720000u, 40u}, // sdc -> Latn
     {0x9C720000u,  1u}, // sdh -> Arab
-    {0x73650000u, 41u}, // se -> Latn
-    {0x94920000u, 41u}, // sef -> Latn
-    {0x9C920000u, 41u}, // seh -> Latn
-    {0xA0920000u, 41u}, // sei -> Latn
-    {0xC8920000u, 41u}, // ses -> Latn
-    {0x73670000u, 41u}, // sg -> Latn
-    {0x80D20000u, 56u}, // sga -> Ogam
-    {0xC8D20000u, 41u}, // sgs -> Latn
-    {0x73680000u, 41u}, // sh -> Latn
-    {0xA0F20000u, 77u}, // shi -> Tfng
-    {0xB4F20000u, 53u}, // shn -> Mymr
+    {0x73650000u, 40u}, // se -> Latn
+    {0x94920000u, 40u}, // sef -> Latn
+    {0x9C920000u, 40u}, // seh -> Latn
+    {0xA0920000u, 40u}, // sei -> Latn
+    {0xC8920000u, 40u}, // ses -> Latn
+    {0x73670000u, 40u}, // sg -> Latn
+    {0x80D20000u, 55u}, // sga -> Ogam
+    {0xC8D20000u, 40u}, // sgs -> Latn
+    {0xD8D20000u, 18u}, // sgw -> Ethi
+    {0xE4D20000u, 40u}, // sgz -> Latn
+    {0x73680000u, 40u}, // sh -> Latn
+    {0xA0F20000u, 78u}, // shi -> Tfng
+    {0xA8F20000u, 40u}, // shk -> Latn
+    {0xB4F20000u, 52u}, // shn -> Mymr
+    {0xD0F20000u,  1u}, // shu -> Arab
     {0x73690000u, 69u}, // si -> Sinh
-    {0x8D120000u, 41u}, // sid -> Latn
-    {0x736B0000u, 41u}, // sk -> Latn
+    {0x8D120000u, 40u}, // sid -> Latn
+    {0x99120000u, 40u}, // sig -> Latn
+    {0xAD120000u, 40u}, // sil -> Latn
+    {0xB1120000u, 40u}, // sim -> Latn
+    {0xC5320000u, 40u}, // sjr -> Latn
+    {0x736B0000u, 40u}, // sk -> Latn
+    {0x89520000u, 40u}, // skc -> Latn
     {0xC5520000u,  1u}, // skr -> Arab
-    {0x736C0000u, 41u}, // sl -> Latn
-    {0xA1720000u, 41u}, // sli -> Latn
-    {0xE1720000u, 41u}, // sly -> Latn
-    {0x736D0000u, 41u}, // sm -> Latn
-    {0x81920000u, 41u}, // sma -> Latn
-    {0xA5920000u, 41u}, // smj -> Latn
-    {0xB5920000u, 41u}, // smn -> Latn
+    {0xC9520000u, 40u}, // sks -> Latn
+    {0x736C0000u, 40u}, // sl -> Latn
+    {0x8D720000u, 40u}, // sld -> Latn
+    {0xA1720000u, 40u}, // sli -> Latn
+    {0xAD720000u, 40u}, // sll -> Latn
+    {0xE1720000u, 40u}, // sly -> Latn
+    {0x736D0000u, 40u}, // sm -> Latn
+    {0x81920000u, 40u}, // sma -> Latn
+    {0xA5920000u, 40u}, // smj -> Latn
+    {0xB5920000u, 40u}, // smn -> Latn
     {0xBD920000u, 65u}, // smp -> Samr
-    {0xC9920000u, 41u}, // sms -> Latn
-    {0x736E0000u, 41u}, // sn -> Latn
-    {0xA9B20000u, 41u}, // snk -> Latn
-    {0x736F0000u, 41u}, // so -> Latn
-    {0xD1D20000u, 79u}, // sou -> Thai
-    {0x73710000u, 41u}, // sq -> Latn
+    {0xC1920000u, 40u}, // smq -> Latn
+    {0xC9920000u, 40u}, // sms -> Latn
+    {0x736E0000u, 40u}, // sn -> Latn
+    {0x89B20000u, 40u}, // snc -> Latn
+    {0xA9B20000u, 40u}, // snk -> Latn
+    {0xBDB20000u, 40u}, // snp -> Latn
+    {0xDDB20000u, 40u}, // snx -> Latn
+    {0xE1B20000u, 40u}, // sny -> Latn
+    {0x736F0000u, 40u}, // so -> Latn
+    {0xA9D20000u, 40u}, // sok -> Latn
+    {0xC1D20000u, 40u}, // soq -> Latn
+    {0xD1D20000u, 80u}, // sou -> Thai
+    {0xE1D20000u, 40u}, // soy -> Latn
+    {0x8DF20000u, 40u}, // spd -> Latn
+    {0xADF20000u, 40u}, // spl -> Latn
+    {0xC9F20000u, 40u}, // sps -> Latn
+    {0x73710000u, 40u}, // sq -> Latn
     {0x73720000u, 15u}, // sr -> Cyrl
-    {0x73724D45u, 41u}, // sr-ME -> Latn
-    {0x7372524Fu, 41u}, // sr-RO -> Latn
-    {0x73725255u, 41u}, // sr-RU -> Latn
-    {0x73725452u, 41u}, // sr-TR -> Latn
+    {0x73724D45u, 40u}, // sr-ME -> Latn
+    {0x7372524Fu, 40u}, // sr-RO -> Latn
+    {0x73725255u, 40u}, // sr-RU -> Latn
+    {0x73725452u, 40u}, // sr-TR -> Latn
     {0x86320000u, 70u}, // srb -> Sora
-    {0xB6320000u, 41u}, // srn -> Latn
-    {0xC6320000u, 41u}, // srr -> Latn
+    {0xB6320000u, 40u}, // srn -> Latn
+    {0xC6320000u, 40u}, // srr -> Latn
     {0xDE320000u, 16u}, // srx -> Deva
-    {0x73730000u, 41u}, // ss -> Latn
-    {0xE2520000u, 41u}, // ssy -> Latn
-    {0x73740000u, 41u}, // st -> Latn
-    {0xC2720000u, 41u}, // stq -> Latn
-    {0x73750000u, 41u}, // su -> Latn
-    {0xAA920000u, 41u}, // suk -> Latn
-    {0xCA920000u, 41u}, // sus -> Latn
-    {0x73760000u, 41u}, // sv -> Latn
-    {0x73770000u, 41u}, // sw -> Latn
+    {0x73730000u, 40u}, // ss -> Latn
+    {0x8E520000u, 40u}, // ssd -> Latn
+    {0x9A520000u, 40u}, // ssg -> Latn
+    {0xE2520000u, 40u}, // ssy -> Latn
+    {0x73740000u, 40u}, // st -> Latn
+    {0xAA720000u, 40u}, // stk -> Latn
+    {0xC2720000u, 40u}, // stq -> Latn
+    {0x73750000u, 40u}, // su -> Latn
+    {0x82920000u, 40u}, // sua -> Latn
+    {0x92920000u, 40u}, // sue -> Latn
+    {0xAA920000u, 40u}, // suk -> Latn
+    {0xC6920000u, 40u}, // sur -> Latn
+    {0xCA920000u, 40u}, // sus -> Latn
+    {0x73760000u, 40u}, // sv -> Latn
+    {0x73770000u, 40u}, // sw -> Latn
     {0x86D20000u,  1u}, // swb -> Arab
-    {0x8AD20000u, 41u}, // swc -> Latn
-    {0x9AD20000u, 41u}, // swg -> Latn
+    {0x8AD20000u, 40u}, // swc -> Latn
+    {0x9AD20000u, 40u}, // swg -> Latn
+    {0xBED20000u, 40u}, // swp -> Latn
     {0xD6D20000u, 16u}, // swv -> Deva
-    {0xB6F20000u, 41u}, // sxn -> Latn
+    {0xB6F20000u, 40u}, // sxn -> Latn
+    {0xDAF20000u, 40u}, // sxw -> Latn
     {0xAF120000u,  7u}, // syl -> Beng
     {0xC7120000u, 71u}, // syr -> Syrc
-    {0xAF320000u, 41u}, // szl -> Latn
+    {0xAF320000u, 40u}, // szl -> Latn
     {0x74610000u, 74u}, // ta -> Taml
     {0xA4130000u, 16u}, // taj -> Deva
-    {0xD8330000u, 41u}, // tbw -> Latn
+    {0xAC130000u, 40u}, // tal -> Latn
+    {0xB4130000u, 40u}, // tan -> Latn
+    {0xC0130000u, 40u}, // taq -> Latn
+    {0x88330000u, 40u}, // tbc -> Latn
+    {0x8C330000u, 40u}, // tbd -> Latn
+    {0x94330000u, 40u}, // tbf -> Latn
+    {0x98330000u, 40u}, // tbg -> Latn
+    {0xB8330000u, 40u}, // tbo -> Latn
+    {0xD8330000u, 40u}, // tbw -> Latn
+    {0xE4330000u, 40u}, // tbz -> Latn
+    {0xA0530000u, 40u}, // tci -> Latn
     {0xE0530000u, 36u}, // tcy -> Knda
     {0x8C730000u, 72u}, // tdd -> Tale
     {0x98730000u, 16u}, // tdg -> Deva
     {0x9C730000u, 16u}, // tdh -> Deva
-    {0x74650000u, 76u}, // te -> Telu
-    {0xB0930000u, 41u}, // tem -> Latn
-    {0xB8930000u, 41u}, // teo -> Latn
-    {0xCC930000u, 41u}, // tet -> Latn
+    {0x74650000u, 77u}, // te -> Telu
+    {0x8C930000u, 40u}, // ted -> Latn
+    {0xB0930000u, 40u}, // tem -> Latn
+    {0xB8930000u, 40u}, // teo -> Latn
+    {0xCC930000u, 40u}, // tet -> Latn
+    {0xA0B30000u, 40u}, // tfi -> Latn
     {0x74670000u, 15u}, // tg -> Cyrl
     {0x7467504Bu,  1u}, // tg-PK -> Arab
-    {0x74680000u, 79u}, // th -> Thai
+    {0x88D30000u, 40u}, // tgc -> Latn
+    {0xB8D30000u, 40u}, // tgo -> Latn
+    {0xD0D30000u, 40u}, // tgu -> Latn
+    {0x74680000u, 80u}, // th -> Thai
     {0xACF30000u, 16u}, // thl -> Deva
     {0xC0F30000u, 16u}, // thq -> Deva
     {0xC4F30000u, 16u}, // thr -> Deva
     {0x74690000u, 18u}, // ti -> Ethi
+    {0x95130000u, 40u}, // tif -> Latn
     {0x99130000u, 18u}, // tig -> Ethi
-    {0xD5130000u, 41u}, // tiv -> Latn
-    {0x746B0000u, 41u}, // tk -> Latn
-    {0xAD530000u, 41u}, // tkl -> Latn
-    {0xC5530000u, 41u}, // tkr -> Latn
+    {0xA9130000u, 40u}, // tik -> Latn
+    {0xB1130000u, 40u}, // tim -> Latn
+    {0xB9130000u, 40u}, // tio -> Latn
+    {0xD5130000u, 40u}, // tiv -> Latn
+    {0x746B0000u, 40u}, // tk -> Latn
+    {0xAD530000u, 40u}, // tkl -> Latn
+    {0xC5530000u, 40u}, // tkr -> Latn
     {0xCD530000u, 16u}, // tkt -> Deva
-    {0x746C0000u, 41u}, // tl -> Latn
-    {0xE1730000u, 41u}, // tly -> Latn
-    {0x9D930000u, 41u}, // tmh -> Latn
-    {0x746E0000u, 41u}, // tn -> Latn
-    {0x746F0000u, 41u}, // to -> Latn
-    {0x99D30000u, 41u}, // tog -> Latn
-    {0xA1F30000u, 41u}, // tpi -> Latn
-    {0x74720000u, 41u}, // tr -> Latn
-    {0xD2330000u, 41u}, // tru -> Latn
-    {0xD6330000u, 41u}, // trv -> Latn
-    {0x74730000u, 41u}, // ts -> Latn
+    {0x746C0000u, 40u}, // tl -> Latn
+    {0x95730000u, 40u}, // tlf -> Latn
+    {0xDD730000u, 40u}, // tlx -> Latn
+    {0xE1730000u, 40u}, // tly -> Latn
+    {0x9D930000u, 40u}, // tmh -> Latn
+    {0xE1930000u, 40u}, // tmy -> Latn
+    {0x746E0000u, 40u}, // tn -> Latn
+    {0x9DB30000u, 40u}, // tnh -> Latn
+    {0x746F0000u, 40u}, // to -> Latn
+    {0x95D30000u, 40u}, // tof -> Latn
+    {0x99D30000u, 40u}, // tog -> Latn
+    {0xC1D30000u, 40u}, // toq -> Latn
+    {0xA1F30000u, 40u}, // tpi -> Latn
+    {0xB1F30000u, 40u}, // tpm -> Latn
+    {0xE5F30000u, 40u}, // tpz -> Latn
+    {0xBA130000u, 40u}, // tqo -> Latn
+    {0x74720000u, 40u}, // tr -> Latn
+    {0xD2330000u, 40u}, // tru -> Latn
+    {0xD6330000u, 40u}, // trv -> Latn
+    {0xDA330000u,  1u}, // trw -> Arab
+    {0x74730000u, 40u}, // ts -> Latn
     {0x8E530000u, 21u}, // tsd -> Grek
     {0x96530000u, 16u}, // tsf -> Deva
-    {0x9A530000u, 41u}, // tsg -> Latn
-    {0xA6530000u, 80u}, // tsj -> Tibt
+    {0x9A530000u, 40u}, // tsg -> Latn
+    {0xA6530000u, 81u}, // tsj -> Tibt
+    {0xDA530000u, 40u}, // tsw -> Latn
     {0x74740000u, 15u}, // tt -> Cyrl
-    {0xA6730000u, 41u}, // ttj -> Latn
-    {0xCA730000u, 79u}, // tts -> Thai
-    {0xCE730000u, 41u}, // ttt -> Latn
-    {0xB2930000u, 41u}, // tum -> Latn
-    {0xAEB30000u, 41u}, // tvl -> Latn
-    {0xC2D30000u, 41u}, // twq -> Latn
-    {0x74790000u, 41u}, // ty -> Latn
+    {0x8E730000u, 40u}, // ttd -> Latn
+    {0x92730000u, 40u}, // tte -> Latn
+    {0xA6730000u, 40u}, // ttj -> Latn
+    {0xC6730000u, 40u}, // ttr -> Latn
+    {0xCA730000u, 80u}, // tts -> Thai
+    {0xCE730000u, 40u}, // ttt -> Latn
+    {0x9E930000u, 40u}, // tuh -> Latn
+    {0xAE930000u, 40u}, // tul -> Latn
+    {0xB2930000u, 40u}, // tum -> Latn
+    {0xC2930000u, 40u}, // tuq -> Latn
+    {0x8EB30000u, 40u}, // tvd -> Latn
+    {0xAEB30000u, 40u}, // tvl -> Latn
+    {0xD2B30000u, 40u}, // tvu -> Latn
+    {0x9ED30000u, 40u}, // twh -> Latn
+    {0xC2D30000u, 40u}, // twq -> Latn
+    {0x9AF30000u, 75u}, // txg -> Tang
+    {0x74790000u, 40u}, // ty -> Latn
+    {0x83130000u, 40u}, // tya -> Latn
     {0xD7130000u, 15u}, // tyv -> Cyrl
-    {0xB3330000u, 41u}, // tzm -> Latn
+    {0xB3330000u, 40u}, // tzm -> Latn
+    {0xD0340000u, 40u}, // ubu -> Latn
     {0xB0740000u, 15u}, // udm -> Cyrl
     {0x75670000u,  1u}, // ug -> Arab
     {0x75674B5Au, 15u}, // ug-KZ -> Cyrl
     {0x75674D4Eu, 15u}, // ug-MN -> Cyrl
-    {0x80D40000u, 81u}, // uga -> Ugar
+    {0x80D40000u, 82u}, // uga -> Ugar
     {0x756B0000u, 15u}, // uk -> Cyrl
-    {0xA1740000u, 41u}, // uli -> Latn
-    {0x85940000u, 41u}, // umb -> Latn
+    {0xA1740000u, 40u}, // uli -> Latn
+    {0x85940000u, 40u}, // umb -> Latn
     {0xC5B40000u,  7u}, // unr -> Beng
     {0xC5B44E50u, 16u}, // unr-NP -> Deva
     {0xDDB40000u,  7u}, // unx -> Beng
     {0x75720000u,  1u}, // ur -> Arab
-    {0x757A0000u, 41u}, // uz -> Latn
+    {0xA2340000u, 40u}, // uri -> Latn
+    {0xCE340000u, 40u}, // urt -> Latn
+    {0xDA340000u, 40u}, // urw -> Latn
+    {0x82540000u, 40u}, // usa -> Latn
+    {0xC6740000u, 40u}, // utr -> Latn
+    {0x9EB40000u, 40u}, // uvh -> Latn
+    {0xAEB40000u, 40u}, // uvl -> Latn
+    {0x757A0000u, 40u}, // uz -> Latn
     {0x757A4146u,  1u}, // uz-AF -> Arab
     {0x757A434Eu, 15u}, // uz-CN -> Cyrl
-    {0xA0150000u, 82u}, // vai -> Vaii
-    {0x76650000u, 41u}, // ve -> Latn
-    {0x88950000u, 41u}, // vec -> Latn
-    {0xBC950000u, 41u}, // vep -> Latn
-    {0x76690000u, 41u}, // vi -> Latn
-    {0x89150000u, 41u}, // vic -> Latn
-    {0xC9750000u, 41u}, // vls -> Latn
-    {0x95950000u, 41u}, // vmf -> Latn
-    {0xD9950000u, 41u}, // vmw -> Latn
-    {0x766F0000u, 41u}, // vo -> Latn
-    {0xCDD50000u, 41u}, // vot -> Latn
-    {0xBA350000u, 41u}, // vro -> Latn
-    {0xB6950000u, 41u}, // vun -> Latn
-    {0x77610000u, 41u}, // wa -> Latn
-    {0x90160000u, 41u}, // wae -> Latn
+    {0x98150000u, 40u}, // vag -> Latn
+    {0xA0150000u, 83u}, // vai -> Vaii
+    {0xB4150000u, 40u}, // van -> Latn
+    {0x76650000u, 40u}, // ve -> Latn
+    {0x88950000u, 40u}, // vec -> Latn
+    {0xBC950000u, 40u}, // vep -> Latn
+    {0x76690000u, 40u}, // vi -> Latn
+    {0x89150000u, 40u}, // vic -> Latn
+    {0xD5150000u, 40u}, // viv -> Latn
+    {0xC9750000u, 40u}, // vls -> Latn
+    {0x95950000u, 40u}, // vmf -> Latn
+    {0xD9950000u, 40u}, // vmw -> Latn
+    {0x766F0000u, 40u}, // vo -> Latn
+    {0xCDD50000u, 40u}, // vot -> Latn
+    {0xBA350000u, 40u}, // vro -> Latn
+    {0xB6950000u, 40u}, // vun -> Latn
+    {0xCE950000u, 40u}, // vut -> Latn
+    {0x77610000u, 40u}, // wa -> Latn
+    {0x90160000u, 40u}, // wae -> Latn
+    {0xA4160000u, 40u}, // waj -> Latn
     {0xAC160000u, 18u}, // wal -> Ethi
-    {0xC4160000u, 41u}, // war -> Latn
-    {0xBC360000u, 41u}, // wbp -> Latn
-    {0xC0360000u, 76u}, // wbq -> Telu
+    {0xB4160000u, 40u}, // wan -> Latn
+    {0xC4160000u, 40u}, // war -> Latn
+    {0xBC360000u, 40u}, // wbp -> Latn
+    {0xC0360000u, 77u}, // wbq -> Telu
     {0xC4360000u, 16u}, // wbr -> Deva
-    {0xC9760000u, 41u}, // wls -> Latn
+    {0xA0560000u, 40u}, // wci -> Latn
+    {0xC4960000u, 40u}, // wer -> Latn
+    {0xA0D60000u, 40u}, // wgi -> Latn
+    {0x98F60000u, 40u}, // whg -> Latn
+    {0x85160000u, 40u}, // wib -> Latn
+    {0xD1160000u, 40u}, // wiu -> Latn
+    {0xD5160000u, 40u}, // wiv -> Latn
+    {0x81360000u, 40u}, // wja -> Latn
+    {0xA1360000u, 40u}, // wji -> Latn
+    {0xC9760000u, 40u}, // wls -> Latn
+    {0xB9960000u, 40u}, // wmo -> Latn
+    {0x89B60000u, 40u}, // wnc -> Latn
     {0xA1B60000u,  1u}, // wni -> Arab
-    {0x776F0000u, 41u}, // wo -> Latn
+    {0xD1B60000u, 40u}, // wnu -> Latn
+    {0x776F0000u, 40u}, // wo -> Latn
+    {0x85D60000u, 40u}, // wob -> Latn
+    {0xC9D60000u, 40u}, // wos -> Latn
+    {0xCA360000u, 40u}, // wrs -> Latn
+    {0xAA560000u, 40u}, // wsk -> Latn
     {0xB2760000u, 16u}, // wtm -> Deva
     {0xD2960000u, 24u}, // wuu -> Hans
-    {0xD4170000u, 41u}, // xav -> Latn
+    {0xD6960000u, 40u}, // wuv -> Latn
+    {0x82D60000u, 40u}, // wwa -> Latn
+    {0xD4170000u, 40u}, // xav -> Latn
+    {0xA0370000u, 40u}, // xbi -> Latn
     {0xC4570000u, 10u}, // xcr -> Cari
-    {0x78680000u, 41u}, // xh -> Latn
-    {0x89770000u, 45u}, // xlc -> Lyci
-    {0x8D770000u, 46u}, // xld -> Lydi
+    {0xC8970000u, 40u}, // xes -> Latn
+    {0x78680000u, 40u}, // xh -> Latn
+    {0x81770000u, 40u}, // xla -> Latn
+    {0x89770000u, 44u}, // xlc -> Lyci
+    {0x8D770000u, 45u}, // xld -> Lydi
     {0x95970000u, 19u}, // xmf -> Geor
-    {0xB5970000u, 48u}, // xmn -> Mani
-    {0xC5970000u, 49u}, // xmr -> Merc
-    {0x81B70000u, 54u}, // xna -> Narb
+    {0xB5970000u, 47u}, // xmn -> Mani
+    {0xC5970000u, 48u}, // xmr -> Merc
+    {0x81B70000u, 53u}, // xna -> Narb
     {0xC5B70000u, 16u}, // xnr -> Deva
-    {0x99D70000u, 41u}, // xog -> Latn
+    {0x99D70000u, 40u}, // xog -> Latn
+    {0xB5D70000u, 40u}, // xon -> Latn
     {0xC5F70000u, 63u}, // xpr -> Prti
+    {0x86370000u, 40u}, // xrb -> Latn
     {0x82570000u, 66u}, // xsa -> Sarb
+    {0xA2570000u, 40u}, // xsi -> Latn
+    {0xB2570000u, 40u}, // xsm -> Latn
     {0xC6570000u, 16u}, // xsr -> Deva
-    {0xB8180000u, 41u}, // yao -> Latn
-    {0xBC180000u, 41u}, // yap -> Latn
-    {0xD4180000u, 41u}, // yav -> Latn
-    {0x84380000u, 41u}, // ybb -> Latn
+    {0x92D70000u, 40u}, // xwe -> Latn
+    {0xB0180000u, 40u}, // yam -> Latn
+    {0xB8180000u, 40u}, // yao -> Latn
+    {0xBC180000u, 40u}, // yap -> Latn
+    {0xC8180000u, 40u}, // yas -> Latn
+    {0xCC180000u, 40u}, // yat -> Latn
+    {0xD4180000u, 40u}, // yav -> Latn
+    {0xE0180000u, 40u}, // yay -> Latn
+    {0xE4180000u, 40u}, // yaz -> Latn
+    {0x80380000u, 40u}, // yba -> Latn
+    {0x84380000u, 40u}, // ybb -> Latn
+    {0xE0380000u, 40u}, // yby -> Latn
+    {0xC4980000u, 40u}, // yer -> Latn
+    {0xC4D80000u, 40u}, // ygr -> Latn
+    {0xD8D80000u, 40u}, // ygw -> Latn
     {0x79690000u, 27u}, // yi -> Hebr
-    {0x796F0000u, 41u}, // yo -> Latn
-    {0xAE380000u, 41u}, // yrl -> Latn
-    {0x82980000u, 41u}, // yua -> Latn
-    {0x7A610000u, 41u}, // za -> Latn
-    {0x98190000u, 41u}, // zag -> Latn
+    {0xB9580000u, 40u}, // yko -> Latn
+    {0x91780000u, 40u}, // yle -> Latn
+    {0x99780000u, 40u}, // ylg -> Latn
+    {0xAD780000u, 40u}, // yll -> Latn
+    {0xAD980000u, 40u}, // yml -> Latn
+    {0x796F0000u, 40u}, // yo -> Latn
+    {0xB5D80000u, 40u}, // yon -> Latn
+    {0x86380000u, 40u}, // yrb -> Latn
+    {0x92380000u, 40u}, // yre -> Latn
+    {0xAE380000u, 40u}, // yrl -> Latn
+    {0xCA580000u, 40u}, // yss -> Latn
+    {0x82980000u, 40u}, // yua -> Latn
+    {0x92980000u, 25u}, // yue -> Hant
+    {0x9298434Eu, 24u}, // yue-CN -> Hans
+    {0xA6980000u, 40u}, // yuj -> Latn
+    {0xCE980000u, 40u}, // yut -> Latn
+    {0xDA980000u, 40u}, // yuw -> Latn
+    {0x7A610000u, 40u}, // za -> Latn
+    {0x98190000u, 40u}, // zag -> Latn
     {0xA4790000u,  1u}, // zdj -> Arab
-    {0x80990000u, 41u}, // zea -> Latn
-    {0x9CD90000u, 77u}, // zgh -> Tfng
+    {0x80990000u, 40u}, // zea -> Latn
+    {0x9CD90000u, 78u}, // zgh -> Tfng
     {0x7A680000u, 24u}, // zh -> Hans
     {0x7A684155u, 25u}, // zh-AU -> Hant
     {0x7A68424Eu, 25u}, // zh-BN -> Hant
@@ -829,9 +1437,12 @@
     {0x7A685457u, 25u}, // zh-TW -> Hant
     {0x7A685553u, 25u}, // zh-US -> Hant
     {0x7A68564Eu, 25u}, // zh-VN -> Hant
-    {0xA1990000u, 41u}, // zmi -> Latn
-    {0x7A750000u, 41u}, // zu -> Latn
-    {0x83390000u, 41u}, // zza -> Latn
+    {0x81190000u, 40u}, // zia -> Latn
+    {0xB1790000u, 40u}, // zlm -> Latn
+    {0xA1990000u, 40u}, // zmi -> Latn
+    {0x91B90000u, 40u}, // zne -> Latn
+    {0x7A750000u, 40u}, // zu -> Latn
+    {0x83390000u, 40u}, // zza -> Latn
 });
 
 std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
@@ -854,6 +1465,7 @@
     0x616D455445746869llu, // am_Ethi_ET
     0xB9804E474C61746Ellu, // amo_Latn_NG
     0xE5C049444C61746Ellu, // aoz_Latn_ID
+    0x8DE0544741726162llu, // apd_Arab_TG
     0x6172454741726162llu, // ar_Arab_EG
     0x8A20495241726D69llu, // arc_Armi_IR
     0x8A204A4F4E626174llu, // arc_Nbat_JO
@@ -896,7 +1508,6 @@
     0x88C1494E44657661llu, // bgc_Deva_IN
     0xB4C1504B41726162llu, // bgn_Arab_PK
     0xDCC154524772656Bllu, // bgx_Grek_TR
-    0x6268494E4B746869llu, // bh_Kthi_IN
     0x84E1494E44657661llu, // bhb_Deva_IN
     0xA0E1494E44657661llu, // bhi_Deva_IN
     0xA8E150484C61746Ellu, // bhk_Latn_PH
@@ -980,6 +1591,7 @@
     0x864344454C61746Ellu, // dsb_Latn_DE
     0xB2634D4C4C61746Ellu, // dtm_Latn_ML
     0xBE634D594C61746Ellu, // dtp_Latn_MY
+    0xE2634E5044657661llu, // dty_Deva_NP
     0x8283434D4C61746Ellu, // dua_Latn_CM
     0x64764D5654686161llu, // dv_Thaa_MV
     0xBB03534E4C61746Ellu, // dyo_Latn_SN
@@ -1006,6 +1618,7 @@
     0xCEE445534C61746Ellu, // ext_Latn_ES
     0x6661495241726162llu, // fa_Arab_IR
     0xB40547514C61746Ellu, // fan_Latn_GQ
+    0x6666474E41646C6Dllu, // ff_Adlm_GN
     0x6666534E4C61746Ellu, // ff_Latn_SN
     0xB0A54D4C4C61746Ellu, // ffm_Latn_ML
     0x666946494C61746Ellu, // fi_Latn_FI
@@ -1020,7 +1633,9 @@
     0xBE2546524C61746Ellu, // frp_Latn_FR
     0xC62544454C61746Ellu, // frr_Latn_DE
     0xCA2544454C61746Ellu, // frs_Latn_DE
+    0x8685434D41726162llu, // fub_Arab_CM
     0x8E8557464C61746Ellu, // fud_Latn_WF
+    0x9685474E4C61746Ellu, // fuf_Latn_GN
     0xC2854E454C61746Ellu, // fuq_Latn_NE
     0xC68549544C61746Ellu, // fur_Latn_IT
     0xD6854E474C61746Ellu, // fuv_Latn_NG
@@ -1104,7 +1719,6 @@
     0x6A614A504A70616Ellu, // ja_Jpan_JP
     0xB0094A4D4C61746Ellu, // jam_Latn_JM
     0xB8C9434D4C61746Ellu, // jgo_Latn_CM
-    0x6A69554148656272llu, // ji_Hebr_UA
     0x8989545A4C61746Ellu, // jmc_Latn_TZ
     0xAD894E5044657661llu, // jml_Deva_NP
     0xCE89444B4C61746Ellu, // jut_Latn_DK
@@ -1118,9 +1732,11 @@
     0xB00A4B454C61746Ellu, // kam_Latn_KE
     0xB80A4D4C4C61746Ellu, // kao_Latn_ML
     0x8C2A52554379726Cllu, // kbd_Cyrl_RU
+    0xE02A4E4541726162llu, // kby_Arab_NE
     0x984A4E474C61746Ellu, // kcg_Latn_NG
     0xA84A5A574C61746Ellu, // kck_Latn_ZW
     0x906A545A4C61746Ellu, // kde_Latn_TZ
+    0x9C6A544741726162llu, // kdh_Arab_TG
     0xCC6A544854686169llu, // kdt_Thai_TH
     0x808A43564C61746Ellu, // kea_Latn_CV
     0xB48A434D4C61746Ellu, // ken_Latn_CM
@@ -1251,7 +1867,7 @@
     0x6D72494E44657661llu, // mr_Deva_IN
     0x8E2C4E5044657661llu, // mrd_Deva_NP
     0xA62C52554379726Cllu, // mrj_Cyrl_RU
-    0xD22C42444D726F6Fllu, // mru_Mroo_BD
+    0xBA2C42444D726F6Fllu, // mro_Mroo_BD
     0x6D734D594C61746Ellu, // ms_Latn_MY
     0x6D744D544C61746Ellu, // mt_Latn_MT
     0xC66C494E44657661llu, // mtr_Deva_IN
@@ -1308,6 +1924,7 @@
     0x6F6D45544C61746Ellu, // om_Latn_ET
     0x6F72494E4F727961llu, // or_Orya_IN
     0x6F7347454379726Cllu, // os_Cyrl_GE
+    0x824E55534F736765llu, // osa_Osge_US
     0xAA6E4D4E4F726B68llu, // otk_Orkh_MN
     0x7061504B41726162llu, // pa_Arab_PK
     0x7061494E47757275llu, // pa_Guru_IN
@@ -1479,6 +2096,7 @@
     0xB2934D574C61746Ellu, // tum_Latn_MW
     0xAEB354564C61746Ellu, // tvl_Latn_TV
     0xC2D34E454C61746Ellu, // twq_Latn_NE
+    0x9AF3434E54616E67llu, // txg_Tang_CN
     0x747950464C61746Ellu, // ty_Latn_PF
     0xD71352554379726Cllu, // tyv_Cyrl_RU
     0xB3334D414C61746Ellu, // tzm_Latn_MA
@@ -1540,14 +2158,18 @@
     0x796F4E474C61746Ellu, // yo_Latn_NG
     0xAE3842524C61746Ellu, // yrl_Latn_BR
     0x82984D584C61746Ellu, // yua_Latn_MX
+    0x9298434E48616E73llu, // yue_Hans_CN
+    0x9298484B48616E74llu, // yue_Hant_HK
     0x7A61434E4C61746Ellu, // za_Latn_CN
     0x981953444C61746Ellu, // zag_Latn_SD
     0xA4794B4D41726162llu, // zdj_Arab_KM
     0x80994E4C4C61746Ellu, // zea_Latn_NL
     0x9CD94D4154666E67llu, // zgh_Tfng_MA
     0x7A685457426F706Fllu, // zh_Bopo_TW
+    0x7A68545748616E62llu, // zh_Hanb_TW
     0x7A68434E48616E73llu, // zh_Hans_CN
     0x7A68545748616E74llu, // zh_Hant_TW
+    0xB17954474C61746Ellu, // zlm_Latn_TG
     0xA1994D594C61746Ellu, // zmi_Latn_MY
     0x7A755A414C61746Ellu, // zu_Latn_ZA
     0x833954524C61746Ellu, // zza_Latn_TR
@@ -1662,6 +2284,7 @@
     {0x656E5A57u, 0x656E8400u}, // en-ZW -> en-001
     {0x65734152u, 0x6573A424u}, // es-AR -> es-419
     {0x6573424Fu, 0x6573A424u}, // es-BO -> es-419
+    {0x65734252u, 0x6573A424u}, // es-BR -> es-419
     {0x6573434Cu, 0x6573A424u}, // es-CL -> es-419
     {0x6573434Fu, 0x6573A424u}, // es-CO -> es-419
     {0x65734352u, 0x6573A424u}, // es-CR -> es-419
@@ -1681,8 +2304,11 @@
     {0x65735559u, 0x6573A424u}, // es-UY -> es-419
     {0x65735645u, 0x6573A424u}, // es-VE -> es-419
     {0x7074414Fu, 0x70745054u}, // pt-AO -> pt-PT
+    {0x70744348u, 0x70745054u}, // pt-CH -> pt-PT
     {0x70744356u, 0x70745054u}, // pt-CV -> pt-PT
+    {0x70744751u, 0x70745054u}, // pt-GQ -> pt-PT
     {0x70744757u, 0x70745054u}, // pt-GW -> pt-PT
+    {0x70744C55u, 0x70745054u}, // pt-LU -> pt-PT
     {0x70744D4Fu, 0x70745054u}, // pt-MO -> pt-PT
     {0x70744D5Au, 0x70745054u}, // pt-MZ -> pt-PT
     {0x70745354u, 0x70745054u}, // pt-ST -> pt-PT
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 3e8e8a1..0ae50e9 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -131,7 +131,7 @@
                         mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan);
 
     static const mat4 identityMatrix;
-    updateLayer(false, identityMatrix.data);
+    updateLayer(false, GL_NONE, identityMatrix.data);
 
     VkLayer* vkLayer = static_cast<VkLayer*>(mLayer);
     vkLayer->updateTexture();
@@ -139,26 +139,20 @@
 
 void DeferredLayerUpdater::updateLayer(bool forceFilter, GLenum renderTarget,
         const float* textureTransform) {
-    LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL,
-                        "updateLayer non GL backend %x, GL %x, VK %x",
-                        mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan);
-
-    updateLayer(forceFilter, textureTransform);
-
-    GlLayer* glLayer = static_cast<GlLayer*>(mLayer);
-    if (renderTarget != glLayer->getRenderTarget()) {
-        glLayer->setRenderTarget(renderTarget);
-        glLayer->bindTexture();
-        glLayer->setFilter(GL_NEAREST, false, true);
-        glLayer->setWrap(GL_CLAMP_TO_EDGE, false, true);
-    }
-}
-
-void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform) {
     mLayer->setBlend(mBlend);
     mLayer->setForceFilter(forceFilter);
     mLayer->setSize(mWidth, mHeight);
     mLayer->getTexTransform().load(textureTransform);
+
+    if (mLayer->getApi() == Layer::Api::OpenGL) {
+        GlLayer* glLayer = static_cast<GlLayer*>(mLayer);
+        if (renderTarget != glLayer->getRenderTarget()) {
+            glLayer->setRenderTarget(renderTarget);
+            glLayer->bindTexture();
+            glLayer->setFilter(GL_NEAREST, false, true);
+            glLayer->setWrap(GL_CLAMP_TO_EDGE, false, true);
+        }
+    }
 }
 
 void DeferredLayerUpdater::detachSurfaceTexture() {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index ead8314..3814be2 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -114,7 +114,6 @@
 
     void doUpdateTexImage();
     void doUpdateVkTexImage();
-    void updateLayer(bool forceFilter, const float* textureTransform);
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 0702010..09e34bf 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -222,6 +222,12 @@
     return sRenderPipelineType;
 }
 
+#ifdef HWUI_GLES_WRAP_ENABLED
+void Properties::overrideRenderPipelineType(RenderPipelineType type) {
+    sRenderPipelineType = type;
+}
+#endif
+
 bool Properties::isSkiaEnabled() {
     auto renderType = getRenderPipelineType();
     return RenderPipelineType::SkiaGL == renderType
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index b4a3118..6dc0cb3 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -318,11 +318,15 @@
     // any overhead they add
     static bool filterOutTestOverhead;
 
+    // Used for testing only to change the render pipeline.
+#ifdef HWUI_GLES_WRAP_ENABLED
+    static void overrideRenderPipelineType(RenderPipelineType);
+#endif
+
 private:
     static ProfileType sProfileType;
     static bool sDisableProfileBars;
     static RenderPipelineType sRenderPipelineType;
-
 }; // class Caches
 
 }; // namespace uirenderer
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 5f6bcb3..275ce16 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -21,6 +21,9 @@
 
 #include <renderthread/EglManager.h>
 #include <renderthread/OpenGLPipeline.h>
+#include <pipeline/skia/SkiaOpenGLPipeline.h>
+#include <pipeline/skia/SkiaVulkanPipeline.h>
+#include <renderthread/VulkanManager.h>
 #include <utils/Unicode.h>
 #include <SkClipStack.h>
 
@@ -47,10 +50,24 @@
 }
 
 sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
+        renderthread::RenderThread& renderThread) {
+    android::uirenderer::renderthread::IRenderPipeline* pipeline;
+    if (Properties::getRenderPipelineType() == RenderPipelineType::OpenGL) {
+        pipeline = new renderthread::OpenGLPipeline(renderThread);
+    } else if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
+        pipeline = new skiapipeline::SkiaOpenGLPipeline(renderThread);
+    } else {
+        pipeline = new skiapipeline::SkiaVulkanPipeline(renderThread);
+    }
+    sp<DeferredLayerUpdater> layerUpdater = pipeline->createTextureLayer();
+    delete pipeline;
+    return layerUpdater;
+}
+
+sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
         renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
         const SkMatrix& transform) {
-    renderthread::OpenGLPipeline pipeline(renderThread);
-    sp<DeferredLayerUpdater> layerUpdater = pipeline.createTextureLayer();
+    sp<DeferredLayerUpdater> layerUpdater = createTextureLayerUpdater(renderThread);
     layerUpdater->backingLayer()->getTransform().load(transform);
     layerUpdater->setSize(width, height);
     layerUpdater->setTransform(&transform);
@@ -111,12 +128,20 @@
 void TestUtils::TestTask::run() {
     // RenderState only valid once RenderThread is running, so queried here
     renderthread::RenderThread& renderThread = renderthread::RenderThread::getInstance();
-    renderThread.eglManager().initialize();
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        renderThread.vulkanManager().initialize();
+    } else {
+        renderThread.eglManager().initialize();
+    }
 
     rtCallback(renderThread);
 
-    renderThread.renderState().flush(Caches::FlushMode::Full);
-    renderThread.eglManager().destroy();
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        renderThread.vulkanManager().destroy();
+    } else {
+        renderThread.renderState().flush(Caches::FlushMode::Full);
+        renderThread.eglManager().destroy();
+    }
 }
 
 std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 80cbb24..8b287de 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -19,6 +19,7 @@
 #include <DeviceInfo.h>
 #include <DisplayList.h>
 #include <Matrix.h>
+#include <Properties.h>
 #include <Rect.h>
 #include <RenderNode.h>
 #include <hwui/Bitmap.h>
@@ -51,6 +52,31 @@
         } else { \
             ADD_FAILURE() << "ClipState not a rect"; \
         }
+
+#define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \
+    TEST(test_case_name, test_name##_##pipeline) { \
+        RenderPipelineType oldType = Properties::getRenderPipelineType(); \
+        Properties::overrideRenderPipelineType(RenderPipelineType::pipeline); \
+        functionCall; \
+        Properties::overrideRenderPipelineType(oldType); \
+    };
+
+/**
+ * Like gtests' TEST, but only runs with the OpenGL RenderPipelineType
+ */
+#define OPENGL_PIPELINE_TEST(test_case_name, test_name) \
+    class test_case_name##_##test_name##_HwuiTest { \
+    public: \
+        static void doTheThing(); \
+    }; \
+    INNER_PIPELINE_TEST(test_case_name, test_name, OpenGL, \
+            test_case_name##_##test_name##_HwuiTest::doTheThing()) \
+    void test_case_name##_##test_name##_HwuiTest::doTheThing()
+
+#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \
+    INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \
+            TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing))
+
 /**
  * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
  * (for e.g. accessing its RenderState)
@@ -60,9 +86,32 @@
     public: \
         static void doTheThing(renderthread::RenderThread& renderThread); \
     }; \
-    TEST(test_case_name, test_name) { \
-        TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); \
+    void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
+
+/**
+ * Like RENDERTHREAD_TEST, but only runs with the OpenGL RenderPipelineType
+ */
+#define RENDERTHREAD_OPENGL_PIPELINE_TEST(test_case_name, test_name) \
+    class test_case_name##_##test_name##_RenderThreadTest { \
+    public: \
+        static void doTheThing(renderthread::RenderThread& renderThread); \
     }; \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \
+    void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
+
+/**
+ * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes
+ */
+#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \
+    class test_case_name##_##test_name##_RenderThreadTest { \
+    public: \
+        static void doTheThing(renderthread::RenderThread& renderThread); \
+    }; \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); \
     void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
 
 /**
@@ -137,6 +186,9 @@
     }
 
     static sp<DeferredLayerUpdater> createTextureLayerUpdater(
+            renderthread::RenderThread& renderThread);
+
+    static sp<DeferredLayerUpdater> createTextureLayerUpdater(
             renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
             const SkMatrix& transform);
 
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
index d44be7d..9a3b81c 100644
--- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
@@ -80,7 +80,7 @@
             << "Glop(s) expected";
 }
 
-RENDERTHREAD_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) {
     SkPaint strokePaint;
     strokePaint.setStyle(SkPaint::kStroke_Style);
     strokePaint.setStrokeWidth(4);
@@ -113,7 +113,7 @@
     testUnmergedGlopDispatch(renderThread, &ovalOp, textureGlopVerifier);
 }
 
-RENDERTHREAD_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
     SkPaint layerPaint;
     layerPaint.setAlpha(128);
     OffscreenBuffer* buffer = nullptr; // no providing a buffer, should hit rect fallback case
@@ -131,7 +131,7 @@
     return result;
 }
 
-RENDERTHREAD_TEST(BakedOpDispatcher, offsetFlags) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, offsetFlags) {
     Rect bounds(10, 15, 20, 25);
     SkPaint paint;
     SkPaint aaPaint;
@@ -157,7 +157,7 @@
             << "Expect an offset for non-AA lines.";
 }
 
-RENDERTHREAD_TEST(BakedOpDispatcher, renderTextWithShadow) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, renderTextWithShadow) {
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
             [](RenderProperties& props, RecordingCanvas& canvas) {
 
@@ -232,7 +232,7 @@
     return c;
 }
 
-RENDERTHREAD_TEST(BakedOpDispatcher, layerUpdateProperties) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, layerUpdateProperties) {
     for (bool debugOverdraw : { false, true }) {
         for (bool debugLayersUpdates : { false, true }) {
             ScopedProperty<bool> ovdProp(Properties::debugOverdraw, debugOverdraw);
@@ -273,7 +273,7 @@
     }
 }
 
-RENDERTHREAD_TEST(BakedOpDispatcher, pathTextureSnapping) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTextureSnapping) {
     Rect bounds(10, 15, 20, 25);
     SkPaint paint;
     SkPath path;
diff --git a/libs/hwui/tests/unit/BakedOpRendererTests.cpp b/libs/hwui/tests/unit/BakedOpRendererTests.cpp
index 59bd75e..380062a 100644
--- a/libs/hwui/tests/unit/BakedOpRendererTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpRendererTests.cpp
@@ -23,7 +23,7 @@
 
 const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
 
-RENDERTHREAD_TEST(BakedOpRenderer, startRepaintLayer_clear) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, startRepaintLayer_clear) {
     BakedOpRenderer renderer(Caches::getInstance(), renderThread.renderState(), true, sLightInfo);
     OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200u, 200u);
 
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
index d3d80a9..42ba3db 100644
--- a/libs/hwui/tests/unit/CanvasContextTests.cpp
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -46,5 +46,10 @@
 RENDERTHREAD_TEST(CanvasContext, invokeFunctor) {
     TestUtils::MockFunctor functor;
     CanvasContext::invokeFunctor(renderThread, &functor);
-    ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcess);
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        // we currently don't support OpenGL WebViews on the Vulkan backend
+        ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcessNoContext);
+    } else {
+        ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcess);
+    }
 }
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index f1b8882..1ef9dba 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -16,8 +16,8 @@
 
 #include "DeferredLayerUpdater.h"
 #include "GlLayer.h"
+#include "Properties.h"
 
-#include "renderthread/OpenGLPipeline.h"
 #include "tests/common/TestUtils.h"
 
 #include <gtest/gtest.h>
@@ -26,12 +26,10 @@
 using namespace android::uirenderer;
 
 RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) {
-    renderthread::OpenGLPipeline pipeline(renderThread);
-    sp<DeferredLayerUpdater> layerUpdater = pipeline.createTextureLayer();
+    sp<DeferredLayerUpdater> layerUpdater = TestUtils::createTextureLayerUpdater(renderThread);
     layerUpdater->setSize(100, 100);
     layerUpdater->setBlend(true);
 
-
     // updates are deferred so the backing layer should still be in its default state
     if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
         GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
diff --git a/libs/hwui/tests/unit/DeviceInfoTests.cpp b/libs/hwui/tests/unit/DeviceInfoTests.cpp
index 17236bd..af37938 100644
--- a/libs/hwui/tests/unit/DeviceInfoTests.cpp
+++ b/libs/hwui/tests/unit/DeviceInfoTests.cpp
@@ -17,11 +17,12 @@
 #include <DeviceInfo.h>
 
 #include <gtest/gtest.h>
+#include "tests/common/TestUtils.h"
 
 using namespace android;
 using namespace android::uirenderer;
 
-TEST(DeviceInfo, basic) {
+OPENGL_PIPELINE_TEST(DeviceInfo, basic) {
     // can't assert state before init - another test may have initialized the singleton
     DeviceInfo::initialize();
     const DeviceInfo* di = DeviceInfo::get();
diff --git a/libs/hwui/tests/unit/FontRendererTests.cpp b/libs/hwui/tests/unit/FontRendererTests.cpp
index 99080ac..ee20236 100644
--- a/libs/hwui/tests/unit/FontRendererTests.cpp
+++ b/libs/hwui/tests/unit/FontRendererTests.cpp
@@ -28,7 +28,7 @@
     return true;
 }
 
-RENDERTHREAD_TEST(FontRenderer, renderDropShadow) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, renderDropShadow) {
     SkPaint paint;
     paint.setTextSize(10);
     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 71c7516..6f3ed9c 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -109,7 +109,7 @@
 
 class FailRenderer : public TestRendererBase {};
 
-RENDERTHREAD_TEST(FrameBuilder, simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simple) {
     class SimpleTestRenderer : public TestRendererBase {
     public:
         void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -143,7 +143,7 @@
     EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
 }
 
-RENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleStroke) {
     class SimpleStrokeTestRenderer : public TestRendererBase {
     public:
         void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
@@ -171,7 +171,7 @@
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleRejection) {
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
@@ -187,7 +187,7 @@
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleBatching) {
     const int LOOPS = 5;
     class SimpleBatchingTestRenderer : public TestRendererBase {
     public:
@@ -225,7 +225,7 @@
             << "Expect number of ops = 2 * loop count";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNode_translateClip) {
     class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -251,7 +251,7 @@
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNodeScene) {
     class DeferRenderNodeSceneTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -320,7 +320,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_noFbo0) {
     class EmptyNoFbo0TestRenderer : public TestRendererBase {
     public:
         void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -338,7 +338,7 @@
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_withFbo0) {
     class EmptyWithFbo0TestRenderer : public TestRendererBase {
     public:
         void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -364,7 +364,7 @@
             " but fbo0 update lifecycle should still be observed";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_rects) {
     class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -394,7 +394,7 @@
     EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
     static sk_sp<Bitmap> opaqueBitmap(TestUtils::createBitmap(50, 50,
             SkColorType::kRGB_565_SkColorType));
     static sk_sp<Bitmap> transpBitmap(TestUtils::createBitmap(50, 50,
@@ -437,7 +437,7 @@
     EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clippedMerging) {
     class ClippedMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedBitmapOps(const MergedBakedOpList& opList) override {
@@ -479,7 +479,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, regionClipStopsMerge) {
     class RegionClipStopsMergeTestRenderer : public TestRendererBase {
     public:
         void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; }
@@ -508,7 +508,7 @@
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, textMerging) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textMerging) {
     class TextMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -538,7 +538,7 @@
     EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStrikethrough) {
     const int LOOPS = 5;
     class TextStrikethroughTestRenderer : public TestRendererBase {
     public:
@@ -576,7 +576,7 @@
 static auto styles = {
         SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
 
-RENDERTHREAD_TEST(FrameBuilder, textStyle) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStyle) {
     class TextStyleTestRenderer : public TestRendererBase {
     public:
         void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -630,7 +630,7 @@
     EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
     class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
     public:
         void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
@@ -664,7 +664,7 @@
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_combineMatrices) {
     class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
     public:
         void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
@@ -696,10 +696,10 @@
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_reject) {
     auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
             SkMatrix::MakeTrans(5, 5));
-    if (layerUpdater->backingLayer()->getApi() != Layer::Api::OpenGL) return;
+    EXPECT_EQ(Layer::Api::OpenGL, layerUpdater->backingLayer()->getApi());
 
     GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
     glLayer->setRenderTarget(GL_NONE); // Should be rejected
@@ -717,7 +717,7 @@
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, functor_reject) {
     class FunctorTestRenderer : public TestRendererBase {
     public:
         void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
@@ -742,7 +742,7 @@
     EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferColorOp_unbounded) {
     class ColorTestRenderer : public TestRendererBase {
     public:
         void onColorOp(const ColorOp& op, const BakedOpState& state) override {
@@ -767,7 +767,7 @@
     EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
 }
 
-TEST(FrameBuilder, renderNode) {
+OPENGL_PIPELINE_TEST(FrameBuilder, renderNode) {
     class RenderNodeTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -814,7 +814,7 @@
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, clipped) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clipped) {
     class ClippedTestRenderer : public TestRendererBase {
     public:
         void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
@@ -840,7 +840,7 @@
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_simple) {
     class SaveLayerSimpleTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -890,7 +890,7 @@
     EXPECT_EQ(5, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_nested) {
     /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
      * - startTemporaryLayer2, rect2 endLayer2
      * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
@@ -973,7 +973,7 @@
     EXPECT_EQ(12, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_contentRejection) {
         auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
                 [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
@@ -996,7 +996,7 @@
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_simple) {
     class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -1041,7 +1041,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_round) {
     class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -1075,7 +1075,7 @@
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
     class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -1133,7 +1133,7 @@
             << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
     class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -1175,7 +1175,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_reject) {
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         // unclipped savelayer + rect both in area that won't intersect with dirty
@@ -1197,7 +1197,7 @@
  * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
  * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
  */
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_complex) {
     class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
@@ -1262,7 +1262,7 @@
     EXPECT_EQ(13, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_simple) {
     class HwLayerSimpleTestRenderer : public TestRendererBase {
     public:
         void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1326,7 +1326,7 @@
     *layerHandle = nullptr;
 }
 
-RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_complex) {
     /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
      * - startRepaintLayer(child), rect(grey), endLayer
      * - startTemporaryLayer, drawLayer(child), endLayer
@@ -1435,7 +1435,7 @@
 }
 
 
-RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, buildLayer) {
     class BuildLayerTestRenderer : public TestRendererBase {
     public:
         void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1531,7 +1531,7 @@
 
 } // end anonymous namespace
 
-RENDERTHREAD_TEST(FrameBuilder, zReorder) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, zReorder) {
     auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.insertReorderBarrier(true);
@@ -1566,7 +1566,7 @@
     EXPECT_EQ(13, renderer.getIndex());
 };
 
-RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorder) {
     static const int scrollX = 5;
     static const int scrollY = 10;
     class ProjectionReorderTestRenderer : public TestRendererBase {
@@ -1659,7 +1659,7 @@
     EXPECT_EQ(3, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionHwLayer) {
     static const int scrollX = 5;
     static const int scrollY = 10;
     class ProjectionHwLayerTestRenderer : public TestRendererBase {
@@ -1750,7 +1750,7 @@
     *layerHandle = nullptr;
 }
 
-RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionChildScroll) {
     static const int scrollX = 500000;
     static const int scrollY = 0;
     class ProjectionChildScrollTestRenderer : public TestRendererBase {
@@ -1817,7 +1817,7 @@
     });
 }
 
-RENDERTHREAD_TEST(FrameBuilder, shadow) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadow) {
     class ShadowTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1850,7 +1850,7 @@
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowSaveLayer) {
     class ShadowSaveLayerTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -1896,7 +1896,7 @@
     EXPECT_EQ(6, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowHwLayer) {
     class ShadowHwLayerTestRenderer : public TestRendererBase {
     public:
         void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1954,7 +1954,7 @@
     *layerHandle = nullptr;
 }
 
-RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowLayering) {
     class ShadowLayeringTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1981,7 +1981,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, shadowClipping) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowClipping) {
     class ShadowClippingTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -2041,7 +2041,7 @@
     EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
     testProperty([](RenderProperties& properties) {
         properties.setAlpha(0.5f);
         properties.setHasOverlappingRendering(false);
@@ -2050,7 +2050,7 @@
     });
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropClipping) {
     testProperty([](RenderProperties& properties) {
         properties.setClipToBounds(true);
         properties.setClipBounds(Rect(10, 20, 300, 400));
@@ -2060,7 +2060,7 @@
     });
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropRevealClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableRevealClip().set(true, 50, 50, 25);
     }, [](const RectOp& op, const BakedOpState& state) {
@@ -2071,7 +2071,7 @@
     });
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOutlineClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableOutline().setShouldClip(true);
         properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
@@ -2083,7 +2083,7 @@
     });
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropTransform) {
     testProperty([](RenderProperties& properties) {
         properties.setLeftTopRightBottom(10, 10, 110, 110);
 
@@ -2192,7 +2192,7 @@
     ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         properties.setTranslationX(10); // offset rendering content
@@ -2211,7 +2211,7 @@
                 << "expect drawLayer to be translated as part of being clipped";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         // Translate and rotate the view so that the only visible part is the top left corner of
@@ -2230,7 +2230,7 @@
     EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         properties.setPivotX(0);
@@ -2244,7 +2244,7 @@
     EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, clip_replace) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clip_replace) {
     class ClipReplaceTestRenderer : public TestRendererBase {
     public:
         void onColorOp(const ColorOp& op, const BakedOpState& state) override {
@@ -2269,7 +2269,7 @@
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderProjectedInMiddle) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMiddle) {
     /* R is backward projected on B
                 A
                / \
@@ -2299,7 +2299,7 @@
     EXPECT_EQ(3, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderProjectLast) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) {
     /* R is backward projected on E
                   A
                 / | \
@@ -2331,7 +2331,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderNoReceivable) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) {
     /* R is backward projected without receiver
                 A
                / \
@@ -2360,7 +2360,7 @@
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderParentReceivable) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivable) {
     /* R is backward projected on C
                 A
                / \
@@ -2389,7 +2389,7 @@
     EXPECT_EQ(3, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderSameNodeReceivable) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceivable) {
      auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         drawOrderedNode(&canvas, 0, nullptr); //nodeB
@@ -2412,7 +2412,7 @@
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderProjectedSibling) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling) {
     //TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a
     //bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical
     //tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling
@@ -2445,7 +2445,7 @@
     EXPECT_EQ(3, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderProjectedSibling2) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling2) {
     /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
                 A
                 |
@@ -2478,7 +2478,7 @@
     EXPECT_EQ(3, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderGrandparentReceivable) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentReceivable) {
     /* R is backward projected on B
                 A
                 |
@@ -2510,7 +2510,7 @@
     EXPECT_EQ(3, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderTwoReceivables) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) {
     /* B and G are receivables, R is backward projected
                 A
                / \
@@ -2543,7 +2543,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) {
     /* B and G are receivables, G is backward projected
                 A
                / \
@@ -2576,7 +2576,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) {
     /* B and G are receivables, R is backward projected
                 A
                / \
diff --git a/libs/hwui/tests/unit/GlopBuilderTests.cpp b/libs/hwui/tests/unit/GlopBuilderTests.cpp
index ce1db05..caeb6bf 100644
--- a/libs/hwui/tests/unit/GlopBuilderTests.cpp
+++ b/libs/hwui/tests/unit/GlopBuilderTests.cpp
@@ -116,7 +116,7 @@
     return glop;
 }
 
-RENDERTHREAD_TEST(GlopBuilder, rectSnapTest) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(GlopBuilder, rectSnapTest) {
     RenderState& renderState = renderThread.renderState();
     Caches& caches = Caches::getInstance();
     SkPaint paint;
diff --git a/libs/hwui/tests/unit/GradientCacheTests.cpp b/libs/hwui/tests/unit/GradientCacheTests.cpp
index 0ee9647..a3b346f 100644
--- a/libs/hwui/tests/unit/GradientCacheTests.cpp
+++ b/libs/hwui/tests/unit/GradientCacheTests.cpp
@@ -23,7 +23,7 @@
 using namespace android;
 using namespace android::uirenderer;
 
-RENDERTHREAD_TEST(GradientCache, addRemove) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(GradientCache, addRemove) {
     Extensions extensions;
     GradientCache cache(extensions);
     ASSERT_LT(1000u, cache.getMaxSize()) << "Expect non-trivial size";
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
index 06599dd..6c42ca1 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -29,7 +29,7 @@
 const FrameBuilder::LightGeometry sLightGeometery = { {100, 100, 100}, 50};
 const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
 
-RENDERTHREAD_TEST(LeakCheck, saveLayer_overdrawRejection) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayer_overdrawRejection) {
     auto node = TestUtils::createNode(0, 0, 100, 100,
             [](RenderProperties& props, Canvas& canvas) {
         canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
@@ -49,7 +49,7 @@
     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayerUnclipped_simple) {
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, Canvas& canvas) {
         canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
diff --git a/libs/hwui/tests/unit/MeshStateTests.cpp b/libs/hwui/tests/unit/MeshStateTests.cpp
index 0881fa2..511d6d2 100644
--- a/libs/hwui/tests/unit/MeshStateTests.cpp
+++ b/libs/hwui/tests/unit/MeshStateTests.cpp
@@ -24,7 +24,7 @@
 using namespace android::uirenderer;
 using namespace testing;
 
-RENDERTHREAD_TEST(MeshState, genOrUpdate) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(MeshState, genOrUpdate) {
     debug::ScopedReplaceDriver<debug::MockGlesDriver> driverRef;
     auto& mockGlDriver = driverRef.get();
     EXPECT_CALL(mockGlDriver, glGenBuffers_(_, _)).WillOnce(SetArgPointee<1>(35));
@@ -33,4 +33,4 @@
 
     GLuint buffer = 0;
     renderThread.renderState().meshState().genOrUpdateMeshBuffer(&buffer, 10, nullptr, GL_DYNAMIC_DRAW);
-}
\ No newline at end of file
+}
diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
index b7950aa..6cd595a 100644
--- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
@@ -30,7 +30,7 @@
     EXPECT_EQ(1024u, OffscreenBuffer::computeIdealDimension(1000));
 }
 
-RENDERTHREAD_TEST(OffscreenBuffer, construct) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, construct) {
     OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 49u, 149u);
     EXPECT_EQ(49u, layer.viewportWidth);
     EXPECT_EQ(149u, layer.viewportHeight);
@@ -41,7 +41,7 @@
     EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
 }
 
-RENDERTHREAD_TEST(OffscreenBuffer, getTextureCoordinates) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, getTextureCoordinates) {
     OffscreenBuffer layerAligned(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
     EXPECT_EQ(Rect(0, 1, 1, 0),
             layerAligned.getTextureCoordinates());
@@ -51,7 +51,7 @@
             layerUnaligned.getTextureCoordinates());
 }
 
-RENDERTHREAD_TEST(OffscreenBuffer, dirty) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, dirty) {
     OffscreenBuffer buffer(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
     buffer.dirty(Rect(-100, -100, 100, 100));
     EXPECT_EQ(android::Rect(100, 100), buffer.region.getBounds());
@@ -65,7 +65,7 @@
             << "pool must read size from Properties";
 }
 
-RENDERTHREAD_TEST(OffscreenBufferPool, getPutClear) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, getPutClear) {
     OffscreenBufferPool pool;
 
     auto layer = pool.get(renderThread.renderState(), 100u, 200u);
@@ -88,7 +88,7 @@
     EXPECT_EQ(0u, pool.getCount());
 }
 
-RENDERTHREAD_TEST(OffscreenBufferPool, resize) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, resize) {
     OffscreenBufferPool pool;
 
     auto layer = pool.get(renderThread.renderState(), 64u, 64u);
@@ -123,7 +123,7 @@
     pool.putOrDelete(layer2);
 }
 
-RENDERTHREAD_TEST(OffscreenBufferPool, putAndDestroy) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, putAndDestroy) {
     OffscreenBufferPool pool;
     // layer too big to return to the pool
     // Note: this relies on the fact that the pool won't reject based on max texture size
@@ -133,7 +133,7 @@
     EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead)
 }
 
-RENDERTHREAD_TEST(OffscreenBufferPool, clear) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, clear) {
     EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
     OffscreenBufferPool pool;
 
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 4a73383..124f5fa 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -47,7 +47,13 @@
     opValidator(*(dl->getOps()[0]));
 }
 
-TEST(RecordingCanvas, emptyPlayback) {
+// The RecordingCanvas is only ever used by the OpenGL RenderPipeline and never when Skia is in use.
+// Thus, even though many of these test are not directly dependent on the current RenderPipeline, we
+// set them all to be OPENGL_PIPELINE_TESTs in case the underlying code in RecordingCanvas ever
+// changes to require the use of the OPENGL_PIPELINE. Currently the textureLayer test is the only
+// test that requires being an OPENGL_PIPELINE_TEST.
+
+OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPlayback) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.restore();
@@ -55,7 +61,7 @@
     playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
 }
 
-TEST(RecordingCanvas, clipRect) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, clipRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
@@ -71,7 +77,7 @@
             << "Clip should be serialized once";
 }
 
-TEST(RecordingCanvas, emptyClipRect) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, emptyClipRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
@@ -82,7 +88,7 @@
     ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected.";
 }
 
-TEST(RecordingCanvas, emptyPaintRejection) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPaintRejection) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         SkPaint emptyPaint;
         emptyPaint.setColor(Color::Transparent);
@@ -103,7 +109,7 @@
     EXPECT_EQ(0u, dl->getOps().size()) << "Op should be rejected";
 }
 
-TEST(RecordingCanvas, drawArc) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawArc) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint());
         canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint());
@@ -119,7 +125,7 @@
     EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds);
 }
 
-TEST(RecordingCanvas, drawLines) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawLines) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
         paint.setStrokeWidth(20); // doesn't affect recorded bounds - would be resolved at bake time
@@ -136,7 +142,7 @@
             << "unmapped bounds must be size of line, and not outset for stroke width";
 }
 
-TEST(RecordingCanvas, drawRect) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         canvas.drawRect(10, 20, 90, 180, SkPaint());
     });
@@ -148,7 +154,7 @@
     EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
 }
 
-TEST(RecordingCanvas, drawRoundRect) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawRoundRect) {
     // Round case - stays rounded
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         canvas.drawRoundRect(0, 0, 100, 100, 10, 10, SkPaint());
@@ -165,7 +171,7 @@
         << "Non-rounded rects should be converted";
 }
 
-TEST(RecordingCanvas, drawGlyphs) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
         paint.setAntiAlias(true);
@@ -186,7 +192,7 @@
     ASSERT_EQ(1, count);
 }
 
-TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
         paint.setAntiAlias(true);
@@ -218,7 +224,7 @@
     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
 }
 
-TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
         paint.setAntiAlias(true);
@@ -248,7 +254,7 @@
     ASSERT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, drawColor) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawColor) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.drawColor(Color::Black, SkBlendMode::kSrcOver);
     });
@@ -260,7 +266,7 @@
     EXPECT_TRUE(op.unmappedBounds.isEmpty()) << "Expect undefined recorded bounds";
 }
 
-TEST(RecordingCanvas, backgroundAndImage) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, backgroundAndImage) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
         SkPaint paint;
@@ -312,7 +318,7 @@
     ASSERT_EQ(2, count);
 }
 
-RENDERTHREAD_TEST(RecordingCanvas, textureLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(RecordingCanvas, textureLayer) {
     auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
             SkMatrix::MakeTrans(5, 5));
 
@@ -327,7 +333,7 @@
     });
 }
 
-TEST(RecordingCanvas, saveLayer_simple) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simple) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(10, 20, 190, 180, SkPaint());
@@ -361,7 +367,7 @@
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_rounding) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rounding) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
             canvas.saveLayerAlpha(10.25f, 10.75f, 89.25f, 89.75f, 128, SaveFlags::ClipToLayer);
             canvas.drawRect(20, 20, 80, 80, SkPaint());
@@ -391,7 +397,7 @@
         EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_missingRestore) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_missingRestore) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(0, 0, 200, 200, SkPaint());
@@ -406,7 +412,7 @@
     EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer";
 }
 
-TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
         canvas.drawRect(10, 20, 190, 180, SkPaint());
@@ -438,7 +444,7 @@
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_addClipFlag) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_addClipFlag) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(10, 20, 190, 180, SkClipOp::kIntersect);
@@ -457,7 +463,7 @@
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_viewportCrop) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_viewportCrop) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         // shouldn't matter, since saveLayer will clip to its bounds
         canvas.clipRect(-1000, -1000, 1000, 1000, SkClipOp::kReplace);
@@ -481,7 +487,7 @@
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.translate(100, 100);
@@ -507,7 +513,7 @@
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_rotateClipped) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateClipped) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.translate(100, 100);
@@ -545,7 +551,7 @@
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_rejectBegin) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rejectBegin) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.translate(0, -20); // avoid identity case
@@ -560,7 +566,7 @@
     ASSERT_EQ(0u, dl->getOps().size()) << "Begin/Rect/End should all be rejected.";
 }
 
-TEST(RecordingCanvas, drawRenderNode_rejection) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_rejection) {
     auto child = TestUtils::createNode(50, 50, 150, 150,
             [](RenderProperties& props, Canvas& canvas) {
         SkPaint paint;
@@ -575,7 +581,7 @@
     ASSERT_TRUE(dl->isEmpty());
 }
 
-TEST(RecordingCanvas, drawRenderNode_projection) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_projection) {
     sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150,
             [](RenderProperties& props, Canvas& canvas) {
         SkPaint paint;
@@ -618,7 +624,7 @@
     }
 }
 
-TEST(RecordingCanvas, firstClipWillReplace) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, firstClipWillReplace) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         // since no explicit clip set on canvas, this should be the one observed on op:
@@ -635,7 +641,7 @@
     EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip);
 }
 
-TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(-10, -10, 110, 110, SkClipOp::kReplace);
@@ -648,7 +654,7 @@
     EXPECT_TRUE(dl->getOps()[0]->localClip->intersectWithRoot);
 }
 
-TEST(RecordingCanvas, insertReorderBarrier) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.insertReorderBarrier(true);
@@ -669,7 +675,7 @@
     EXPECT_TRUE(chunks[1].reorderChildren);
 }
 
-TEST(RecordingCanvas, insertReorderBarrier_clip) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier_clip) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         // first chunk: no recorded clip
         canvas.insertReorderBarrier(true);
@@ -699,7 +705,7 @@
     EXPECT_EQ(Rect(200, 200), chunks[2].reorderClip->rect);
 }
 
-TEST(RecordingCanvas, refPaint) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, refPaint) {
     SkPaint paint;
 
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) {
@@ -727,7 +733,7 @@
     EXPECT_NE(&paint, ops[2]->paint);
 }
 
-TEST(RecordingCanvas, refBitmap) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmap) {
     sk_sp<Bitmap> bitmap(TestUtils::createBitmap(100, 100));
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
         canvas.drawBitmap(*bitmap, 0, 0, nullptr);
@@ -736,7 +742,7 @@
     EXPECT_EQ(1u, bitmaps.size());
 }
 
-TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
     sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
         SkPaint paint;
@@ -755,7 +761,7 @@
     EXPECT_EQ(1u, bitmaps.size());
 }
 
-TEST(RecordingCanvas, refBitmapInShader_composeShader) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_composeShader) {
     sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
         SkPaint paint;
@@ -785,7 +791,7 @@
     EXPECT_EQ(1u, bitmaps.size());
 }
 
-TEST(RecordingCanvas, drawText) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawText) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         Paint paint;
         paint.setAntiAlias(true);
@@ -807,7 +813,7 @@
     ASSERT_EQ(1, count);
 }
 
-TEST(RecordingCanvas, drawTextInHighContrast) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawTextInHighContrast) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.setHighContrastText(true);
         Paint paint;
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 331a6ac..ab8e4e1 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -137,10 +137,11 @@
 RENDERTHREAD_TEST(RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
 
     VectorDrawable::Group* group = new VectorDrawable::Group();
-    VectorDrawableRoot* vectorDrawable = new VectorDrawableRoot(group);
+    sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
+
     auto rootNode = TestUtils::createNode(0, 0, 200, 400,
             [&](RenderProperties& props, Canvas& canvas) {
-        canvas.drawVectorDrawable(vectorDrawable);
+        canvas.drawVectorDrawable(vectorDrawable.get());
     });
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
@@ -164,7 +165,5 @@
     EXPECT_FALSE(info.layerUpdateQueue->entries().empty());
     EXPECT_EQ(rootNode.get(), info.layerUpdateQueue->entries().at(0).renderNode);
     EXPECT_EQ(uirenderer::Rect(0, 0, 200, 400), info.layerUpdateQueue->entries().at(0).damage);
-
-    delete vectorDrawable;
     canvasContext->destroy(nullptr);
 }
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 95f9974..0ac09ac 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -28,7 +28,7 @@
  * Verify that we get the same culling bounds for text for (1) drawing glyphs
  * directly to a Canvas or (2) going through a SkPicture as an intermediate step.
  */
-TEST(SkiaCanvasProxy, drawGlyphsViaPicture) {
+OPENGL_PIPELINE_TEST(SkiaCanvasProxy, drawGlyphsViaPicture) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         // setup test variables
         SkPaint paint;
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 899758a..8f6fc8b 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -118,7 +118,7 @@
     }
 };
 
-RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 494585a..0b8c2a9 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -36,7 +36,7 @@
 using namespace android::uirenderer::renderthread;
 using namespace android::uirenderer::skiapipeline;
 
-RENDERTHREAD_TEST(SkiaPipeline, renderFrame) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
     auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1,
         [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
             redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -55,7 +55,7 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckOpaque) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
     auto halfGreenNode = TestUtils::createSkiaNode(0, 0, 2, 2,
         [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
             SkPaint greenPaint;
@@ -80,7 +80,7 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
     auto redNode = TestUtils::createSkiaNode(0, 0, 2, 2,
         [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
             redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -101,7 +101,7 @@
     ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, renderLayer) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
     auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1,
         [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
             redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -144,7 +144,7 @@
     blueNode->setLayerSurface(sk_sp<SkSurface>());
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, renderOverdraw) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) {
     ScopedProperty<bool> prop(Properties::debugOverdraw, true);
 
     auto whiteNode = TestUtils::createSkiaNode(0, 0, 1, 1,
@@ -218,7 +218,7 @@
 };
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, deferRenderNodeScene) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) {
     class DeferTestCanvas : public SkCanvas {
     public:
         DeferTestCanvas() : SkCanvas(800, 600) {}
@@ -284,7 +284,7 @@
     EXPECT_EQ(4, surface->canvas()->mDrawCounter);
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, clipped) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) {
     static const int CANVAS_WIDTH = 200;
     static const int CANVAS_HEIGHT = 200;
     class ClippedTestCanvas : public SkCanvas {
@@ -315,7 +315,7 @@
     EXPECT_EQ(1, surface->canvas()->mDrawCounter);
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, clip_replace) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
     static const int CANVAS_WIDTH = 50;
     static const int CANVAS_HEIGHT = 50;
     class ClipReplaceTestCanvas : public SkCanvas {
diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
index e7171c8..92d9d3d 100644
--- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
@@ -73,7 +73,7 @@
 
 }
 
-RENDERTHREAD_TEST(RenderNodeDrawable, renderPropClipping) {
+TEST(RenderNodeDrawable, renderPropClipping) {
     testProperty([](RenderProperties& properties) {
         properties.setClipToBounds(true);
         properties.setClipBounds(android::uirenderer::Rect(10, 20, 300, 400));
@@ -83,7 +83,7 @@
     });
 }
 
-RENDERTHREAD_TEST(RenderNodeDrawable, renderPropRevealClip) {
+TEST(RenderNodeDrawable, renderPropRevealClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableRevealClip().set(true, 50, 50, 25);
     }, [](const SkCanvas& canvas) {
@@ -98,7 +98,7 @@
     });
 }
 
-RENDERTHREAD_TEST(RenderNodeDrawable, renderPropOutlineClip) {
+TEST(RenderNodeDrawable, renderPropOutlineClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableOutline().setShouldClip(true);
         properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
@@ -114,7 +114,7 @@
     });
 }
 
-RENDERTHREAD_TEST(RenderNodeDrawable, renderPropTransform) {
+TEST(RenderNodeDrawable, renderPropTransform) {
     testProperty([](RenderProperties& properties) {
         properties.setLeftTopRightBottom(10, 10, 110, 110);
 
diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
index 0d26df2..8312bda 100644
--- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
+++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
@@ -26,7 +26,7 @@
 using namespace android;
 using namespace android::uirenderer;
 
-RENDERTHREAD_TEST(TextDropShadowCache, addRemove) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) {
     SkPaint paint;
     paint.setTextSize(20);
 
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index b38a07f..dd9c6a7 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -283,10 +283,19 @@
 
     /**
      * @hide
-     * FIXME return a player proxy instead, make systemApi
-     * @return
+     * Return a proxy for the player associated with this playback configuration
+     * @return a proxy player
      */
-    public IPlayer getPlayerProxy() {
+    @SystemApi
+    public PlayerProxy getPlayerProxy() {
+        return mIPlayerShell == null ? null : new PlayerProxy(this);
+    }
+
+    /**
+     * @hide
+     * @return the IPlayer interface for the associated player
+     */
+    IPlayer getIPlayer() {
         return mIPlayerShell == null ? null : mIPlayerShell.getIPlayer();
     }
 
diff --git a/media/java/android/media/PlayerProxy.java b/media/java/android/media/PlayerProxy.java
new file mode 100644
index 0000000..171be27
--- /dev/null
+++ b/media/java/android/media/PlayerProxy.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.IllegalArgumentException;
+import java.util.Objects;
+
+/**
+ * Class to remotely control a player.
+ * @hide
+ */
+@SystemApi
+public class PlayerProxy {
+
+    private final static String TAG = "PlayerProxy";
+    private final static boolean DEBUG = false;
+
+    private final AudioPlaybackConfiguration mConf; // never null
+
+    /**
+     * @hide
+     * Constructor. Proxy for this player associated with this AudioPlaybackConfiguration
+     * @param conf the configuration being proxied.
+     */
+    PlayerProxy(@NonNull AudioPlaybackConfiguration apc) {
+        if (apc == null) {
+            throw new IllegalArgumentException("Illegal null AudioPlaybackConfiguration");
+        }
+        mConf = apc;
+    };
+
+    //=====================================================================
+    // Methods matching the IPlayer interface
+    /**
+     * @hide
+     * @throws IllegalStateException
+     */
+    @SystemApi
+    public void start() throws IllegalStateException {
+        try {
+            mConf.getIPlayer().start();
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for start operation, player already released?", e);
+        }
+    }
+
+    /**
+     * @hide
+     * @throws IllegalStateException
+     */
+    @SystemApi
+    public void pause() throws IllegalStateException {
+        try {
+            mConf.getIPlayer().pause();
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for pause operation, player already released?", e);
+        }
+    }
+
+    /**
+     * @hide
+     * @throws IllegalStateException
+     */
+    @SystemApi
+    public void stop() throws IllegalStateException {
+        try {
+            mConf.getIPlayer().stop();
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for stop operation, player already released?", e);
+        }
+    }
+
+    /**
+     * @hide
+     * @throws IllegalStateException
+     */
+    @SystemApi
+    public void setVolume(float vol) throws IllegalStateException {
+        try {
+            mConf.getIPlayer().setVolume(vol);
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for setVolume operation, player already released?", e);
+        }
+    }
+
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index e1657c7..f8f4f2a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -29,6 +29,7 @@
 import android.util.Slog;
 import android.util.TypedValue;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.GridLayout;
 import android.widget.TextClock;
 import android.widget.TextView;
@@ -48,6 +49,7 @@
     private TextClock mDateView;
     private TextClock mClockView;
     private TextView mOwnerInfo;
+    private ViewGroup mClockContainer;
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
@@ -105,6 +107,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mClockContainer = (ViewGroup) findViewById(R.id.keyguard_clock_container);
         mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
         mDateView = (TextClock) findViewById(R.id.date_view);
         mClockView = (TextClock) findViewById(R.id.clock_view);
@@ -167,6 +170,11 @@
         }
     }
 
+    public int getClockBottom() {
+        return mClockView.getBottom() +
+                ((MarginLayoutParams) mClockView.getLayoutParams()).bottomMargin;
+    }
+
     public static String formatNextAlarm(Context context, AlarmManager.AlarmClockInfo info) {
         if (info == null) {
             return "";
@@ -260,4 +268,15 @@
             cacheKey = key;
         }
     }
+
+    public void setDark(boolean dark) {
+        final int N = mClockContainer.getChildCount();
+        for (int i = 0; i < N; i++) {
+            View child = mClockContainer.getChildAt(i);
+            if (child == mClockView) {
+                continue;
+            }
+            child.setAlpha(dark ? 0 : 1);
+        }
+    }
 }
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 78d211f..6ffdbcb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -75,6 +75,9 @@
     <!-- Height of a large notification in the status bar -->
     <dimen name="notification_max_height">284dp</dimen>
 
+    <!-- Height of an ambient notification on ambient display -->
+    <dimen name="notification_ambient_height">400dp</dimen>
+
     <!-- Height of a heads up notification in the status bar for legacy custom views -->
     <dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index e081b53..00d2298 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -37,7 +37,7 @@
 
     interface Callback {
         default void onNewNotifications() {}
-        default void onBuzzBeepBlinked() {}
+        default void onNotificationHeadsUp() {}
         default void onNotificationLight(boolean on) {}
         default void onPowerSaveChanged(boolean active) {}
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 84b22ea..db5a392 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -309,7 +309,7 @@
 
     private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
         @Override
-        public void onBuzzBeepBlinked() {
+        public void onNotificationHeadsUp() {
             onNotification();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 0a56eac..2f8a78b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -23,6 +23,7 @@
 import static android.view.View.MeasureSpec;
 
 import android.app.ActivityManager;
+import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
@@ -60,6 +61,7 @@
 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
 import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
 import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
+import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
 import com.android.systemui.recents.misc.DozeTrigger;
 import com.android.systemui.recents.misc.ForegroundThread;
 import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -131,6 +133,11 @@
                 loader.loadTasks(mContext, plan, launchOpts);
             }
         }
+
+        @Override
+        public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
+            EventBus.getDefault().send(new TaskSnapshotChangedEvent(taskId, snapshot));
+        }
     }
 
     protected static RecentsTaskLoadPlan sInstanceLoadPlan;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java
new file mode 100644
index 0000000..07c3b3d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.recents.events.ui;
+
+import android.app.ActivityManager.TaskSnapshot;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Sent when a task snapshot has changed.
+ */
+public class TaskSnapshotChangedEvent extends EventBus.Event {
+
+    public final int taskId;
+    public final TaskSnapshot taskSnapshot;
+
+    public TaskSnapshotChangedEvent(int taskId, TaskSnapshot taskSnapshot) {
+        this.taskId = taskId;
+        this.taskSnapshot = taskSnapshot;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 3587b89..a2b86d1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -24,7 +24,9 @@
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager;
+import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.IActivityManager;
@@ -148,6 +150,7 @@
      */
     public abstract static class TaskStackListener {
         public void onTaskStackChanged() { }
+        public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
         public void onActivityPinned() { }
         public void onPinnedActivityRestartAttempt() { }
         public void onPinnedStackAnimationEnded() { }
@@ -202,6 +205,12 @@
         public void onTaskProfileLocked(int taskId, int userId) {
             mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
         }
+
+        @Override
+        public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)
+                throws RemoteException {
+            mHandler.obtainMessage(H.ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
+        }
     };
 
     /**
@@ -591,17 +600,17 @@
     /** Returns the top task thumbnail for the given task id */
     public ThumbnailData getTaskThumbnail(int taskId) {
         if (mAm == null) return null;
-        ThumbnailData thumbnailData = new ThumbnailData();
 
         // If we are mocking, then just return a dummy thumbnail
         if (RecentsDebugFlags.Static.EnableMockTasks) {
+            ThumbnailData thumbnailData = new ThumbnailData();
             thumbnailData.thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth,
                     mDummyThumbnailHeight, Bitmap.Config.ARGB_8888);
             thumbnailData.thumbnail.eraseColor(0xff333333);
             return thumbnailData;
         }
 
-        getThumbnail(taskId, thumbnailData);
+        ThumbnailData thumbnailData = getThumbnail(taskId);
         if (thumbnailData.thumbnail != null && !ActivityManager.ENABLE_TASK_SNAPSHOTS) {
             thumbnailData.thumbnail.setHasAlpha(false);
             // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
@@ -621,11 +630,12 @@
     /**
      * Returns a task thumbnail from the activity manager
      */
-    public void getThumbnail(int taskId, ThumbnailData thumbnailDataOut) {
+    public @NonNull ThumbnailData getThumbnail(int taskId) {
         if (mAm == null) {
-            return;
+            return new ThumbnailData();
         }
 
+        final ThumbnailData thumbnailData;
         if (ActivityManager.ENABLE_TASK_SNAPSHOTS) {
             ActivityManager.TaskSnapshot snapshot = null;
             try {
@@ -634,16 +644,14 @@
                 Log.w(TAG, "Failed to retrieve snapshot", e);
             }
             if (snapshot != null) {
-                thumbnailDataOut.thumbnail = Bitmap.createHardwareBitmap(snapshot.getSnapshot());
-                thumbnailDataOut.orientation = snapshot.getOrientation();
-                thumbnailDataOut.insets.set(snapshot.getContentInsets());
+                thumbnailData = ThumbnailData.createFromTaskSnapshot(snapshot);
             } else {
-                thumbnailDataOut.thumbnail = null;
+                return new ThumbnailData();
             }
         } else {
             ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
             if (taskThumbnail == null) {
-                return;
+                return new ThumbnailData();
             }
 
             Bitmap thumbnail = taskThumbnail.mainThumbnail;
@@ -658,10 +666,12 @@
                 } catch (IOException e) {
                 }
             }
-            thumbnailDataOut.thumbnail = thumbnail;
-            thumbnailDataOut.orientation = taskThumbnail.thumbnailInfo.screenOrientation;
-            thumbnailDataOut.insets.setEmpty();
+            thumbnailData = new ThumbnailData();
+            thumbnailData.thumbnail = thumbnail;
+            thumbnailData.orientation = taskThumbnail.thumbnailInfo.screenOrientation;
+            thumbnailData.insets.setEmpty();
         }
+        return thumbnailData;
     }
 
     /**
@@ -1172,12 +1182,13 @@
 
     private final class H extends Handler {
         private static final int ON_TASK_STACK_CHANGED = 1;
-        private static final int ON_ACTIVITY_PINNED = 2;
-        private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 3;
-        private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4;
-        private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5;
-        private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 6;
-        private static final int ON_TASK_PROFILE_LOCKED = 7;
+        private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
+        private static final int ON_ACTIVITY_PINNED = 3;
+        private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4;
+        private static final int ON_PINNED_STACK_ANIMATION_ENDED = 5;
+        private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6;
+        private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7;
+        private static final int ON_TASK_PROFILE_LOCKED = 8;
 
         @Override
         public void handleMessage(Message msg) {
@@ -1188,6 +1199,13 @@
                     }
                     break;
                 }
+                case ON_TASK_SNAPSHOT_CHANGED: {
+                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                        mTaskStackListeners.get(i).onTaskSnapshotChanged(msg.arg1,
+                                (TaskSnapshot) msg.obj);
+                    }
+                    break;
+                }
                 case ON_ACTIVITY_PINNED: {
                     for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                         mTaskStackListeners.get(i).onActivityPinned();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java b/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
index 18735ac..09a3712 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.model;
 
+import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 
@@ -23,7 +24,17 @@
  * Data for a single thumbnail.
  */
 public class ThumbnailData {
+
+    // TODO: Make these final once the non-snapshot path is removed.
     public Bitmap thumbnail;
     public int orientation;
     public final Rect insets = new Rect();
+
+    public static ThumbnailData createFromTaskSnapshot(TaskSnapshot snapshot) {
+        ThumbnailData out = new ThumbnailData();
+        out.thumbnail = Bitmap.createHardwareBitmap(snapshot.getSnapshot());
+        out.insets.set(snapshot.getContentInsets());
+        out.orientation = snapshot.getOrientation();
+        return out;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 5f37349..e41a718 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -22,7 +22,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
-import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Outline;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index e3bf1df..bae5daa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -35,6 +35,9 @@
 import android.view.ViewDebug;
 
 import com.android.systemui.R;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.EventBus.Event;
+import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.ThumbnailData;
@@ -347,6 +350,7 @@
             mBgFillPaint.setColor(t.colorBackground);
         }
         mLockedPaint.setColor(t.colorPrimary);
+        EventBus.getDefault().register(this);
     }
 
     /**
@@ -361,6 +365,14 @@
     void unbindFromTask() {
         mTask = null;
         setThumbnail(null);
+        EventBus.getDefault().unregister(this);
+    }
+
+    public final void onBusEvent(TaskSnapshotChangedEvent event) {
+        if (mTask == null || event.taskId != mTask.key.id || event.taskSnapshot == null) {
+            return;
+        }
+        setThumbnail(ThumbnailData.createFromTaskSnapshot(event.taskSnapshot));
     }
 
     public void dump(String prefix, PrintWriter writer) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 19034f8..faf143e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1132,6 +1132,11 @@
                 // Post to ensure the the guts are properly laid out.
                 guts.post(new Runnable() {
                     public void run() {
+                        if (row.getWindowToken() == null) {
+                            Log.e(TAG, "Trying to show notification guts, but not attached to "
+                                    + "window");
+                            return;
+                        }
                         dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */,
                                 false /* animate */);
                         guts.setVisibility(View.VISIBLE);
@@ -1519,6 +1524,7 @@
         final RemoteViews bigContentView = entry.cachedBigContentView;
         final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
         final RemoteViews publicContentView = entry.cachedPublicContentView;
+        final RemoteViews ambientContentView = entry.cachedAmbientContentView;
 
         if (contentView == null) {
             Log.v(TAG, "no contentView for: " + sbn.getNotification());
@@ -1599,6 +1605,7 @@
         View bigContentViewLocal = null;
         View headsUpContentViewLocal = null;
         View publicViewLocal = null;
+        View ambientViewLocal = null;
         try {
             contentViewLocal = contentView.apply(
                     sbn.getPackageContext(mContext),
@@ -1621,6 +1628,11 @@
                         sbn.getPackageContext(mContext),
                         contentContainerPublic, mOnClickHandler);
             }
+            if (ambientContentView != null) {
+                ambientViewLocal = ambientContentView.apply(
+                        sbn.getPackageContext(mContext),
+                        contentContainer, mOnClickHandler);
+            }
 
             if (contentViewLocal != null) {
                 contentViewLocal.setIsRootNamespace(true);
@@ -1638,6 +1650,11 @@
                 publicViewLocal.setIsRootNamespace(true);
                 contentContainerPublic.setContractedChild(publicViewLocal);
             }
+
+            if (ambientViewLocal != null) {
+                ambientViewLocal.setIsRootNamespace(true);
+                contentContainer.setAmbientChild(ambientViewLocal);
+            }
         }
         catch (RuntimeException e) {
             final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
@@ -2134,6 +2151,7 @@
                 row.setOnKeyguard(false);
                 row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
             }
+            entry.row.setShowAmbient(isDozing());
             int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
                     entry.notification) && !entry.row.isRemoved();
@@ -2171,6 +2189,10 @@
         mStackScroller.changeViewPosition(mNotificationShelf, mStackScroller.getChildCount() - 3);
     }
 
+    public boolean isDozing() {
+        return false;
+    }
+
     public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
         return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
     }
@@ -2383,7 +2405,7 @@
             Log.d(TAG, "failed to query dream manager", e);
         }
 
-        if (!inUse) {
+        if (!inUse && !isDozing()) {
             if (DEBUG) {
                 Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 173a110..93c48f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -74,6 +74,7 @@
     private int mMaxHeadsUpHeight;
     private int mNotificationMinHeight;
     private int mNotificationMaxHeight;
+    private int mNotificationAmbientHeight;
     private int mIncreasedPaddingBetweenElements;
 
     /** Does this row contain layouts that can adapt to row expansion */
@@ -197,6 +198,7 @@
     private float mContentTransformationAmount;
     private boolean mIconsVisible = true;
     private boolean mAboveShelf;
+    private boolean mShowAmbient;
     private boolean mIsLastChild;
     private Runnable mOnDismissRunnable;
 
@@ -326,7 +328,8 @@
                         != com.android.internal.R.id.status_bar_latest_event_content;
         int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
                 : mMaxHeadsUpHeight;
-        layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
+        layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
+                mNotificationAmbientHeight);
     }
 
     public StatusBarNotification getStatusBarNotification() {
@@ -954,6 +957,7 @@
         mNotificationMinHeightLegacy = getFontScaledHeight(R.dimen.notification_min_height_legacy);
         mNotificationMinHeight = getFontScaledHeight(R.dimen.notification_min_height);
         mNotificationMaxHeight = getFontScaledHeight(R.dimen.notification_max_height);
+        mNotificationAmbientHeight = getFontScaledHeight(R.dimen.notification_ambient_height);
         mMaxHeadsUpHeightLegacy = getFontScaledHeight(
                 R.dimen.notification_max_heads_up_height_legacy);
         mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height);
@@ -1353,6 +1357,8 @@
             return mGuts.getHeight();
         } else if ((isChildInGroup() && !isGroupExpanded())) {
             return mPrivateLayout.getMinHeight();
+        } else if (mShowAmbient) {
+            return getAmbientHeight();
         } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
             return getMinHeight();
         } else if (mIsSummaryWithChildren && !mOnKeyguard) {
@@ -1683,6 +1689,13 @@
         return showingLayout.getMinHeight();
     }
 
+    private int getAmbientHeight() {
+        NotificationContentView showingLayout = getShowingLayout();
+        return showingLayout.getAmbientChild() != null
+                ? showingLayout.getAmbientChild().getHeight()
+                : getCollapsedHeight();
+    }
+
     @Override
     public int getCollapsedHeight() {
         if (mIsSummaryWithChildren && !mShowingPublic) {
@@ -1879,6 +1892,13 @@
         return mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf);
     }
 
+    public void setShowAmbient(boolean showAmbient) {
+        if (showAmbient != mShowAmbient) {
+            mShowAmbient = showAmbient;
+            notifyHeightChanged(false /* needsAnimation */);
+        }
+    }
+
     public void setAboveShelf(boolean aboveShelf) {
         mAboveShelf = aboveShelf;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 9ce2115..077303a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -52,6 +52,7 @@
     private static final int VISIBLE_TYPE_EXPANDED = 1;
     private static final int VISIBLE_TYPE_HEADSUP = 2;
     private static final int VISIBLE_TYPE_SINGLELINE = 3;
+    private static final int VISIBLE_TYPE_AMBIENT = 4;
     public static final int UNDEFINED = -1;
 
     private final Rect mClipBounds = new Rect();
@@ -62,6 +63,7 @@
     private View mExpandedChild;
     private View mHeadsUpChild;
     private HybridNotificationView mSingleLineView;
+    private View mAmbientChild;
 
     private RemoteInputView mExpandedRemoteInput;
     private RemoteInputView mHeadsUpRemoteInput;
@@ -69,6 +71,7 @@
     private NotificationViewWrapper mContractedWrapper;
     private NotificationViewWrapper mExpandedWrapper;
     private NotificationViewWrapper mHeadsUpWrapper;
+    private NotificationViewWrapper mAmbientWrapper;
     private HybridGroupManager mHybridGroupManager;
     private int mClipTopAmount;
     private int mContentHeight;
@@ -81,6 +84,7 @@
     private int mSmallHeight;
     private int mHeadsUpHeight;
     private int mNotificationMaxHeight;
+    private int mNotificationAmbientHeight;
     private StatusBarNotification mStatusBarNotification;
     private NotificationGroupManager mGroupManager;
     private RemoteInputController mRemoteInputController;
@@ -136,10 +140,12 @@
         reset();
     }
 
-    public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
+    public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
+            int ambientHeight) {
         mSmallHeight = smallHeight;
         mHeadsUpHeight = headsUpMaxHeight;
         mNotificationMaxHeight = maxHeight;
+        mNotificationAmbientHeight = ambientHeight;
     }
 
     @Override
@@ -215,6 +221,17 @@
                     MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
             maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
         }
+        if (mAmbientChild != null) {
+            int size = Math.min(maxSize, mNotificationAmbientHeight);
+            ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
+            if (layoutParams.height >= 0) {
+                // An actual height is set
+                size = Math.min(size, layoutParams.height);
+            }
+            mAmbientChild.measure(widthMeasureSpec,
+                    MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
+            maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
+        }
         int ownHeight = Math.min(maxChildHeight, maxSize);
         setMeasuredDimension(width, ownHeight);
     }
@@ -339,6 +356,10 @@
         return mHeadsUpChild;
     }
 
+    public View getAmbientChild() {
+        return mAmbientChild;
+    }
+
     public void setContractedChild(View child) {
         if (mContractedChild != null) {
             mContractedChild.animate().cancel();
@@ -373,6 +394,17 @@
                 mContainingNotification);
     }
 
+    public void setAmbientChild(View child) {
+        if (mAmbientChild != null) {
+            mAmbientChild.animate().cancel();
+            removeView(mAmbientChild);
+        }
+        addView(child);
+        mAmbientChild = child;
+        mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
+                mContainingNotification);
+    }
+
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
@@ -447,6 +479,11 @@
                         com.android.internal.R.dimen.notification_action_list_height);
         }
 
+        if (isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
+            return mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
+                    com.android.internal.R.dimen.notification_action_list_height);
+        }
+
         // Transition between heads-up & expanded, or pinned.
         if (mHeadsUpChild != null && mExpandedChild != null) {
             boolean transitioningBetweenHunAndExpanded =
@@ -651,39 +688,23 @@
     }
 
     private void forceUpdateVisibilities() {
-        boolean contractedVisible = mVisibleType == VISIBLE_TYPE_CONTRACTED
-                || mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED;
-        boolean expandedVisible = mVisibleType == VISIBLE_TYPE_EXPANDED
-                || mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED;
-        boolean headsUpVisible = mVisibleType == VISIBLE_TYPE_HEADSUP
-                || mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP;
-        boolean singleLineVisible = mVisibleType == VISIBLE_TYPE_SINGLELINE
-                || mTransformationStartVisibleType == VISIBLE_TYPE_SINGLELINE;
-        if (!contractedVisible) {
-            mContractedChild.setVisibility(View.INVISIBLE);
+        forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
+        forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
+    }
+
+    private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
+        if (view == null) {
+            return;
+        }
+        boolean visible = mVisibleType == type
+                || mTransformationStartVisibleType == type;
+        if (!visible) {
+            view.setVisibility(INVISIBLE);
         } else {
-            mContractedWrapper.setVisible(true);
-        }
-        if (mExpandedChild != null) {
-            if (!expandedVisible) {
-                mExpandedChild.setVisibility(View.INVISIBLE);
-            } else {
-                mExpandedWrapper.setVisible(true);
-            }
-        }
-        if (mHeadsUpChild != null) {
-            if (!headsUpVisible) {
-                mHeadsUpChild.setVisibility(View.INVISIBLE);
-            } else {
-                mHeadsUpWrapper.setVisible(true);
-            }
-        }
-        if (mSingleLineView != null) {
-            if (!singleLineVisible) {
-                mSingleLineView.setVisibility(View.INVISIBLE);
-            } else {
-                mSingleLineView.setVisible(true);
-            }
+            wrapper.setVisible(true);
         }
     }
 
@@ -717,19 +738,22 @@
     }
 
     private void updateViewVisibilities(int visibleType) {
-        boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
-        mContractedWrapper.setVisible(contractedVisible);
-        if (mExpandedChild != null) {
-            boolean expandedVisible = visibleType == VISIBLE_TYPE_EXPANDED;
-            mExpandedWrapper.setVisible(expandedVisible);
-        }
-        if (mHeadsUpChild != null) {
-            boolean headsUpVisible = visibleType == VISIBLE_TYPE_HEADSUP;
-            mHeadsUpWrapper.setVisible(headsUpVisible);
-        }
-        if (mSingleLineView != null) {
-            boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
-            mSingleLineView.setVisible(singleLineVisible);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
+                mContractedChild, mContractedWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
+                mExpandedChild, mExpandedWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
+                mHeadsUpChild, mHeadsUpWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
+                mSingleLineView, mSingleLineView);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
+                mAmbientChild, mAmbientWrapper);
+    }
+
+    private void updateViewVisibility(int visibleType, int type, View view,
+            TransformableView wrapper) {
+        if (view != null) {
+            wrapper.setVisible(visibleType == type);
         }
     }
 
@@ -779,6 +803,8 @@
                 return mHeadsUpWrapper;
             case VISIBLE_TYPE_SINGLELINE:
                 return mSingleLineView;
+            case VISIBLE_TYPE_AMBIENT:
+                return mAmbientWrapper;
             default:
                 return mContractedWrapper;
         }
@@ -796,6 +822,8 @@
                 return mHeadsUpChild;
             case VISIBLE_TYPE_SINGLELINE:
                 return mSingleLineView;
+            case VISIBLE_TYPE_AMBIENT:
+                return mAmbientChild;
             default:
                 return mContractedChild;
         }
@@ -809,6 +837,8 @@
                 return mHeadsUpWrapper;
             case VISIBLE_TYPE_CONTRACTED:
                 return mContractedWrapper;
+            case VISIBLE_TYPE_AMBIENT:
+                return mAmbientWrapper;
             default:
                 return null;
         }
@@ -818,6 +848,10 @@
      * @return one of the static enum types in this view, calculated form the current state
      */
     public int calculateVisibleType() {
+        if (mDark && !mIsChildInGroup) {
+            // TODO: Handle notification groups
+            return VISIBLE_TYPE_AMBIENT;
+        }
         if (mUserExpanding) {
             int height = !mIsChildInGroup || isGroupExpanded()
                     || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
@@ -890,6 +924,7 @@
         if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
             mSingleLineView.setDark(dark, fade, delay);
         }
+        selectLayout(!dark && fade /* animate */, false /* force */);
     }
 
     public void setHeadsUp(boolean headsUp) {
@@ -942,6 +977,9 @@
         if (mHeadsUpChild != null) {
             mHeadsUpWrapper.notifyContentUpdated(entry.notification);
         }
+        if (mAmbientChild != null) {
+            mAmbientWrapper.notifyContentUpdated(entry.notification);
+        }
         updateShowingLegacyBackground();
         mForceSelectNextLayout = true;
         setDark(mDark, false /* animate */, 0 /* delay */);
@@ -1128,6 +1166,9 @@
         if (header == null && mHeadsUpChild != null) {
             header = mHeadsUpWrapper.getNotificationHeader();
         }
+        if (header == null && mAmbientChild != null) {
+            header = mAmbientWrapper.getNotificationHeader();
+        }
         return header;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index c06e639..458daf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -67,6 +67,7 @@
         public RemoteViews cachedBigContentView;
         public RemoteViews cachedHeadsUpContentView;
         public RemoteViews cachedPublicContentView;
+        public RemoteViews cachedAmbientContentView;
         public CharSequence remoteInputText;
         private int mCachedContrastColor = COLOR_INVALID;
         private int mCachedContrastColorIsFor = COLOR_INVALID;
@@ -126,6 +127,8 @@
                         updatedNotificationBuilder.createHeadsUpContentView();
                 final RemoteViews newPublicNotification
                         = updatedNotificationBuilder.makePublicContentView();
+                final RemoteViews newAmbientNotification
+                        = updatedNotificationBuilder.makeAmbientNotification();
 
                 boolean sameCustomView = Objects.equals(
                         notification.getNotification().extras.getBoolean(
@@ -136,11 +139,13 @@
                         && compareRemoteViews(cachedBigContentView, newBigContentView)
                         && compareRemoteViews(cachedHeadsUpContentView, newHeadsUpContentView)
                         && compareRemoteViews(cachedPublicContentView, newPublicNotification)
+                        && compareRemoteViews(cachedAmbientContentView, newAmbientNotification)
                         && sameCustomView;
                 cachedPublicContentView = newPublicNotification;
                 cachedHeadsUpContentView = newHeadsUpContentView;
                 cachedBigContentView = newBigContentView;
                 cachedContentView = newContentView;
+                cachedAmbientContentView = newAmbientNotification;
             } else {
                 final Notification.Builder builder
                         = Notification.Builder.recoverBuilder(ctx, notification.getNotification());
@@ -149,6 +154,7 @@
                 cachedBigContentView = builder.createBigContentView();
                 cachedHeadsUpContentView = builder.createHeadsUpContentView();
                 cachedPublicContentView = builder.makePublicContentView();
+                cachedAmbientContentView = builder.makeAmbientNotification();
 
                 applyInPlace = false;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 64c4949..e8e9d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -129,6 +129,7 @@
         } else {
             mViewInvertHelper.update(dark);
         }
+        mShelfIcons.setAmbient(dark);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 7ca2df9..b984c0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -116,8 +116,10 @@
 
     private void resolveTemplateViews(StatusBarNotification notification) {
         mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
-        mPicture.setTag(ImageTransformState.ICON_TAG,
-                notification.getNotification().getLargeIcon());
+        if (mPicture != null) {
+            mPicture.setTag(ImageTransformState.ICON_TAG,
+                    notification.getNotification().getLargeIcon());
+        }
         mTitle = (TextView) mView.findViewById(com.android.internal.R.id.title);
         mText = (TextView) mView.findViewById(com.android.internal.R.id.text);
         final View progress = mView.findViewById(com.android.internal.R.id.progress);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 5047041..a4e5916 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -39,6 +39,7 @@
     private VisibilityLocationProvider mVisibilityLocationProvider;
     private ArraySet<View> mAllowedReorderViews = new ArraySet<>();
     private ArraySet<View> mAddedChildren = new ArraySet<>();
+    private boolean mPulsing;
 
     /**
      * Add a callback to invoke when reordering is allowed again.
@@ -67,8 +68,16 @@
         updateReorderingAllowed();
     }
 
+    /**
+     * @param pulsing whether we are currently pulsing for ambient display.
+     */
+    public void setPulsing(boolean pulsing) {
+        mPulsing = pulsing;
+        updateReorderingAllowed();
+    }
+
     private void updateReorderingAllowed() {
-        boolean reorderingAllowed = !mScreenOn || !mPanelExpanded;
+        boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing;
         boolean changed = reorderingAllowed && !mReorderingAllowed;
         mReorderingAllowed = reorderingAllowed;
         if (changed) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 01ffe01..b78f748 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -26,6 +26,7 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.Interpolators;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
@@ -41,7 +42,9 @@
     private final Handler mHandler = new Handler();
     private final ScrimController mScrimController;
 
+    private final Context mContext;
     private final View mStackScroller;
+    private final NotificationPanelView mNotificationPanelView;
 
     private boolean mDozing;
     private DozeHost.PulseCallback mPulseCallback;
@@ -52,10 +55,12 @@
     private float mBehindTarget;
 
     public DozeScrimController(ScrimController scrimController, Context context,
-            View stackScroller) {
+            View stackScroller, NotificationPanelView notificationPanelView) {
+        mContext = context;
         mStackScroller = stackScroller;
         mScrimController = scrimController;
         mDozeParameters = new DozeParameters(context);
+        mNotificationPanelView = notificationPanelView;
     }
 
     public void setDozing(boolean dozing, boolean animate) {
@@ -65,10 +70,7 @@
             abortAnimations();
             mScrimController.setDozeBehindAlpha(1f);
             mScrimController.setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? 0f : 1f);
-            if (mDozeParameters.getAlwaysOn()) {
-                mStackScroller.setAlpha(0f);
-                mHandler.postDelayed(() -> mStackScroller.setAlpha(0f), 30);
-            }
+            mNotificationPanelView.setDark(true);
         } else {
             cancelPulsing();
             if (animate) {
@@ -83,9 +85,8 @@
                 mScrimController.setDozeBehindAlpha(0f);
                 mScrimController.setDozeInFrontAlpha(0f);
             }
-            if (mDozeParameters.getAlwaysOn()) {
-                mStackScroller.setAlpha(1f);
-            }
+            // TODO: animate
+            mNotificationPanelView.setDark(false);
         }
     }
 
@@ -123,9 +124,6 @@
         if (isPulsing()) {
             final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
                     || mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
-            if (mDozeParameters.getAlwaysOn()) {
-                mStackScroller.setAlpha(1f);
-            }
             startScrimAnimation(true /* inFront */, 0f,
                     mDozeParameters.getPulseInDuration(pickupOrDoubleTap),
                     pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
@@ -291,9 +289,6 @@
         @Override
         public void run() {
             if (DEBUG) Log.d(TAG, "Pulse out finished");
-            if (mDozeParameters.getAlwaysOn()) {
-                mStackScroller.setAlpha(0f);
-            }
             DozeLog.tracePulseFinish();
 
             // Signal that the pulse is all finished so we can turn the screen off now.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 70beac8ea..c78ec83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -68,6 +68,8 @@
     }
 
     private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
+    private int mClockBottom;
+    private boolean mDark;
 
     /**
      * Refreshes the dimension values.
@@ -86,7 +88,8 @@
     }
 
     public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
-            int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount) {
+            int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount,
+            int clockBottom, boolean dark) {
         mMaxKeyguardNotifications = maxKeyguardNotifications;
         mMaxPanelHeight = maxPanelHeight;
         mExpandedHeight = expandedHeight;
@@ -94,6 +97,8 @@
         mHeight = height;
         mKeyguardStatusHeight = keyguardStatusHeight;
         mEmptyDragAmount = emptyDragAmount;
+        mClockBottom = clockBottom;
+        mDark = dark;
     }
 
     public float getMinStackScrollerPadding(int height, int keyguardStatusHeight) {
@@ -115,6 +120,9 @@
                 result.clockY,
                 y + getClockNotificationsPadding() + mKeyguardStatusHeight);
         result.clockAlpha = getClockAlpha(result.clockScale);
+        if (mDark) {
+            result.stackScrollerPadding = mClockBottom + y;
+        }
     }
 
     private float getClockScale(int notificationPadding, int clockY, int startPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 9fb5980..c25a45c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -22,9 +22,7 @@
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.util.AttributeSet;
-import android.util.Property;
 import android.view.View;
-import android.view.animation.Interpolator;
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -32,7 +30,6 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.stack.AnimationFilter;
 import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.HeadsUpAppearInterpolator;
 import com.android.systemui.statusbar.stack.ViewState;
 
 import java.util.HashMap;
@@ -98,6 +95,7 @@
     private int mActualLayoutWidth = NO_VALUE;
     private float mActualPaddingEnd = NO_VALUE;
     private float mActualPaddingStart = NO_VALUE;
+    private boolean mCentered;
     private boolean mChangingViewPositions;
     private int mAddAnimationStartIndex = -1;
     private int mCannedAnimationStartIndex = -1;
@@ -105,6 +103,7 @@
     private int mIconSize;
     private float mOpenedAmount = 0.0f;
     private float mVisualOverflowAdaption;
+    private boolean mDisallowNextAnimation;
 
     public NotificationIconContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -165,6 +164,7 @@
         }
         mAddAnimationStartIndex = -1;
         mCannedAnimationStartIndex = -1;
+        mDisallowNextAnimation = false;
     }
 
     @Override
@@ -310,6 +310,15 @@
                 numDots++;
             }
         }
+        if (mCentered && translationX < getLayoutEnd()) {
+            float delta = (getLayoutEnd() - translationX) / 2;
+            for (int i = 0; i < childCount; i++) {
+                View view = getChildAt(i);
+                IconState iconState = mIconStates.get(view);
+                iconState.xTranslation += delta;
+            }
+        }
+
         if (isLayoutRtl()) {
             for (int i = 0; i < childCount; i++) {
                 View view = getChildAt(i);
@@ -379,6 +388,11 @@
         mChangingViewPositions = changingViewPositions;
     }
 
+    public void setAmbient(boolean ambient) {
+        mCentered = ambient;
+        mDisallowNextAnimation = true;
+    }
+
     public IconState getIconState(StatusBarIconView icon) {
         return mIconStates.get(icon);
     }
@@ -469,7 +483,7 @@
                     animate = true;
                 }
                 icon.setVisibleState(visibleState);
-                if (animate) {
+                if (animate && !mDisallowNextAnimation) {
                     animateTo(icon, animationProperties);
                 } else {
                     super.applyToView(view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index d48819a..3bdd5e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -211,6 +211,7 @@
     private boolean mOpening;
     private int mIndicationBottomPadding;
     private boolean mIsFullWidth;
+    private boolean mDark;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -391,7 +392,9 @@
                     mNotificationStackScroller.getNotGoneChildCount(),
                     getHeight(),
                     mKeyguardStatusView.getHeight(),
-                    mEmptyDragAmount);
+                    mEmptyDragAmount,
+                    mKeyguardStatusView.getClockBottom(),
+                    mDark);
             mClockPositionAlgorithm.run(mClockPositionResult);
             if (animate || mClockAnimator != null) {
                 startClockAnimation(mClockPositionResult.clockY);
@@ -2453,4 +2456,10 @@
             }
         }
     };
+
+    public void setDark(boolean dark) {
+        mDark = dark;
+        mKeyguardStatusView.setDark(dark);
+        positionClockAndNotifications();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 5998ed2..9f12ca7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -74,7 +74,6 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.Process;
@@ -879,7 +878,8 @@
         mHeadsUpManager.addListener(mScrimController);
         mStackScroller.setScrimController(mScrimController);
         mStatusBarView.setScrimController(mScrimController);
-        mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller);
+        mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller,
+                mNotificationPanel);
 
         // Other icons
         mLocationController = new LocationControllerImpl(mContext,
@@ -2401,6 +2401,7 @@
         return getBarState() == StatusBarState.KEYGUARD;
     }
 
+    @Override
     public boolean isDozing() {
         return mDozing;
     }
@@ -2487,6 +2488,9 @@
             }
         } else {
             updateNotificationRanking(null);
+            if (isHeadsUp) {
+                mDozeServiceHost.fireNotificationHeadsUp();
+            }
         }
 
     }
@@ -2886,9 +2890,6 @@
 
     @Override // CommandQueue
     public void buzzBeepBlinked() {
-        if (mDozeServiceHost != null) {
-            mDozeServiceHost.fireBuzzBeepBlinked();
-        }
     }
 
     @Override
@@ -4234,6 +4235,7 @@
         mDozeScrimController.setDozing(mDozing &&
                 mFingerprintUnlockController.getMode()
                         != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING, animate);
+        updateRowStates();
         Trace.endSection();
     }
 
@@ -4904,9 +4906,9 @@
             }
         }
 
-        public void fireBuzzBeepBlinked() {
+        public void fireNotificationHeadsUp() {
             for (Callback callback : mCallbacks) {
-                callback.onBuzzBeepBlinked();
+                callback.onNotificationHeadsUp();
             }
         }
 
@@ -4950,12 +4952,14 @@
                 public void onPulseStarted() {
                     callback.onPulseStarted();
                     mStackScroller.setPulsing(true);
+                    mVisualStabilityManager.setPulsing(true);
                 }
 
                 @Override
                 public void onPulseFinished() {
                     callback.onPulseFinished();
                     mStackScroller.setPulsing(false);
+                    mVisualStabilityManager.setPulsing(false);
                 }
             }, reason);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 9545fd8..395e8f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1883,12 +1883,16 @@
         float previousIncreasedAmount = 0.0f;
         int numShownItems = 0;
         boolean finish = false;
+        int maxDisplayedNotifications = mAmbientState.isDark()
+                ? (mPulsing ? 1 : 0)
+                : mMaxDisplayedNotifications;
+
         for (int i = 0; i < getChildCount(); i++) {
             ExpandableView expandableView = (ExpandableView) getChildAt(i);
             if (expandableView.getVisibility() != View.GONE
                     && !expandableView.hasNoContentHeight()) {
-                if (mMaxDisplayedNotifications != -1
-                        && numShownItems >= mMaxDisplayedNotifications) {
+                if (maxDisplayedNotifications != -1
+                        && numShownItems >= maxDisplayedNotifications) {
                     expandableView = mShelf;
                     finish = true;
                 }
@@ -3486,6 +3490,8 @@
             updateBackground();
             setWillNotDraw(false);
         }
+        updateContentHeight();
+        notifyHeightChangeListener(mShelf);
     }
 
     private void setBackgroundFadeAmount(float fadeAmount) {
@@ -3921,6 +3927,8 @@
     public void setPulsing(boolean pulsing) {
         mPulsing = pulsing;
         updateNotificationAnimationStates();
+        updateContentHeight();
+        notifyHeightChangeListener(mShelf);
     }
 
     public void setFadingOut(boolean fadingOut) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
index be6290b..76bb6c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
@@ -150,4 +150,28 @@
         mVisualStabilityManager.onReorderingFinished();
         assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
     }
+
+    @Test
+    public void testPulsing() {
+        mVisualStabilityManager.setPulsing(true);
+        assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+        mVisualStabilityManager.setPulsing(false);
+        assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+    }
+
+    @Test
+    public void testReorderingAllowedChanges_Pulsing() {
+        mVisualStabilityManager.setPulsing(true);
+        assertEquals(mVisualStabilityManager.isReorderingAllowed(), false);
+        mVisualStabilityManager.setPulsing(false);
+        assertEquals(mVisualStabilityManager.isReorderingAllowed(), true);
+    }
+
+    @Test
+    public void testCallBackCalled_Pulsing() {
+        mVisualStabilityManager.setPulsing(true);
+        mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+        mVisualStabilityManager.setPulsing(false);
+        verify(mCallback).onReorderingAllowed();
+    }
 }
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index cb20eac..d035fa9 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import android.app.ActivityManager;
+import android.app.ActivityManager.TaskSnapshot;
 import android.app.ITaskStackListener;
 import android.app.ActivityManager.TaskDescription;
 import android.content.ComponentName;
@@ -43,6 +45,7 @@
     static final int NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS = 12;
     static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
     static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14;
+    static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15;
 
     // Delay in notifying task stack change listeners (in millis)
     static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -113,6 +116,10 @@
         l.onTaskProfileLocked(m.arg1, m.arg2);
     };
 
+    private final TaskStackConsumer mNotifyTaskSnapshotChanged = (l, m) -> {
+        l.onTaskSnapshotChanged(m.arg1, (TaskSnapshot) m.obj);
+    };
+
     @FunctionalInterface
     public interface TaskStackConsumer {
         void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -170,7 +177,9 @@
                     break;
                 case NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG:
                     forAllRemoteListeners(mNotifyTaskProfileLocked, msg);
-
+                    break;
+                case NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyTaskSnapshotChanged, msg);
                     break;
             }
         }
@@ -348,4 +357,14 @@
         forAllLocalListeners(mNotifyTaskProfileLocked, msg);
         msg.sendToTarget();
     }
+
+    /**
+     * Notify listeners that the snapshot of a task has changed.
+     */
+    void notifyTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG,
+                taskId, 0, snapshot);
+        forAllLocalListeners(mNotifyTaskSnapshotChanged, msg);
+        msg.sendToTarget();
+    }
 }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 4c4c444..a72a958 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -52,6 +52,8 @@
 import com.android.internal.util.XmlUtils;
 
 import com.android.server.wm.TaskWindowContainerController;
+import com.android.server.wm.TaskWindowContainerListener;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -105,7 +107,7 @@
 import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 
-final class TaskRecord extends ConfigurationContainer {
+final class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
@@ -412,8 +414,8 @@
 
         final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
         final Configuration overrideConfig = getOverrideConfiguration();
-        mWindowContainerController = new TaskWindowContainerController(taskId, getStackId(), userId,
-                bounds, overrideConfig, mResizeMode, isHomeTask(), isOnTopLauncher(), onTop,
+        mWindowContainerController = new TaskWindowContainerController(taskId, this, getStackId(),
+                userId, bounds, overrideConfig, mResizeMode, isHomeTask(), isOnTopLauncher(), onTop,
                 showForAllUsers);
     }
 
@@ -429,6 +431,11 @@
         mWindowContainerController = null;
     }
 
+    @Override
+    public void onSnapshotChanged(TaskSnapshot snapshot) {
+        mService.mTaskChangeNotificationController.notifyTaskSnapshotChanged(taskId, snapshot);
+    }
+
     void setResizeMode(int resizeMode) {
         if (mResizeMode == resizeMode) {
             return;
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 927dfd5..4c950de 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -16,6 +16,9 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
@@ -142,7 +145,11 @@
         // message to aid in any subsequent debugging
         if (DBG) Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
 
-        cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback);
+        // The following use of the legacy type system cannot be removed until
+        // after upstream selection no longer finds networks by legacy type.
+        // See also b/34364553.
+        final int apnType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
+        cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, apnType);
     }
 
     public void releaseMobileNetworkRequest() {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 87f4030..fbb39384 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -25,6 +25,7 @@
 import com.android.internal.inputmethod.InputMethodSubtypeHandle;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.R;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
@@ -1700,6 +1701,7 @@
     // Binder call
     @Override
     public void setCustomPointerIcon(PointerIcon icon) {
+        Preconditions.checkNotNull(icon);
         nativeSetCustomPointerIcon(mPtr, icon);
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f09e856..9018302 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3289,7 +3289,9 @@
     private boolean playSound(final NotificationRecord record, Uri soundUri) {
         boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
         // do not play notifications if there is a user of exclusive audio focus
-        if (!mAudioManager.isAudioFocusExclusive()) {
+        // or the device is in vibrate mode
+        if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal()
+                != AudioManager.RINGER_MODE_VIBRATE) {
             final long identity = Binder.clearCallingIdentity();
             try {
                 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 9fc70d6..e646ffc 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -67,6 +67,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManager.EnforcingUser;
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.storage.StorageManager;
@@ -118,6 +119,7 @@
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -162,7 +164,11 @@
     private static final String TAG_USER = "user";
     private static final String TAG_RESTRICTIONS = "restrictions";
     private static final String TAG_DEVICE_POLICY_RESTRICTIONS = "device_policy_restrictions";
+    private static final String TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS =
+            "device_policy_global_restrictions";
+    /** Legacy name for device owner id tag. */
     private static final String TAG_GLOBAL_RESTRICTION_OWNER_ID = "globalRestrictionOwnerUserId";
+    private static final String TAG_DEVICE_OWNER_USER_ID = "deviceOwnerUserId";
     private static final String TAG_ENTRY = "entry";
     private static final String TAG_VALUE = "value";
     private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions";
@@ -202,7 +208,7 @@
     @VisibleForTesting
     static final int MAX_RECENTLY_REMOVED_IDS_SIZE = 100;
 
-    private static final int USER_VERSION = 6;
+    private static final int USER_VERSION = 7;
 
     private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
 
@@ -267,7 +273,7 @@
 
     /**
      * User restrictions set via UserManager.  This doesn't include restrictions set by
-     * device owner / profile owners.
+     * device owner / profile owners. Only non-empty restriction bundles are stored.
      *
      * DO NOT Change existing {@link Bundle} in it.  When changing a restriction for a user,
      * a new {@link Bundle} should always be created and set.  This is because a {@link Bundle}
@@ -305,20 +311,21 @@
 
     /**
      * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
-     * that should be applied to all users, including guests.
+     * that should be applied to all users, including guests. Only non-empty restriction bundles are
+     * stored.
      */
     @GuardedBy("mRestrictionsLock")
-    private Bundle mDevicePolicyGlobalUserRestrictions;
+    private final SparseArray<Bundle> mDevicePolicyGlobalUserRestrictions = new SparseArray<>();
 
     /**
      * Id of the user that set global restrictions.
      */
     @GuardedBy("mRestrictionsLock")
-    private int mGlobalRestrictionOwnerUserId = UserHandle.USER_NULL;
+    private int mDeviceOwnerUserId = UserHandle.USER_NULL;
 
     /**
      * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
-     * for each user.
+     * for each user. Only non-empty restriction bundles are stored.
      */
     @GuardedBy("mRestrictionsLock")
     private final SparseArray<Bundle> mDevicePolicyLocalUserRestrictions = new SparseArray<>();
@@ -1176,39 +1183,36 @@
     }
 
     /**
-     * See {@link UserManagerInternal#setDevicePolicyUserRestrictions(int, Bundle, Bundle)}
+     * See {@link UserManagerInternal#setDevicePolicyUserRestrictions}
      */
-    void setDevicePolicyUserRestrictionsInner(int userId, @NonNull Bundle local,
-            @Nullable Bundle global) {
-        Preconditions.checkNotNull(local);
-        boolean globalChanged = false;
-        boolean localChanged;
+    private void setDevicePolicyUserRestrictionsInner(int userId, @Nullable Bundle restrictions,
+            boolean isDeviceOwner, int cameraRestrictionScope) {
+        final Bundle global = new Bundle();
+        final Bundle local = new Bundle();
+
+        // Sort restrictions into local and global ensuring they don't overlap.
+        UserRestrictionsUtils.sortToGlobalAndLocal(restrictions, isDeviceOwner,
+                cameraRestrictionScope, global, local);
+
+        boolean globalChanged, localChanged;
         synchronized (mRestrictionsLock) {
-            if (global != null) {
-                // Update global.
-                globalChanged = !UserRestrictionsUtils.areEqual(
-                        mDevicePolicyGlobalUserRestrictions, global);
-                if (globalChanged) {
-                    mDevicePolicyGlobalUserRestrictions = global;
-                }
+            // Update global and local restrictions if they were changed.
+            globalChanged = updateRestrictionsIfNeededLR(
+                    userId, global, mDevicePolicyGlobalUserRestrictions);
+            localChanged = updateRestrictionsIfNeededLR(
+                    userId, local, mDevicePolicyLocalUserRestrictions);
+
+            if (isDeviceOwner) {
                 // Remember the global restriction owner userId to be able to make a distinction
                 // in getUserRestrictionSource on who set local policies.
-                mGlobalRestrictionOwnerUserId = userId;
+                mDeviceOwnerUserId = userId;
             } else {
-                if (mGlobalRestrictionOwnerUserId == userId) {
+                if (mDeviceOwnerUserId == userId) {
                     // When profile owner sets restrictions it passes null global bundle and we
                     // reset global restriction owner userId.
                     // This means this user used to have DO, but now the DO is gone and the user
                     // instead has PO.
-                    mGlobalRestrictionOwnerUserId = UserHandle.USER_NULL;
-                }
-            }
-            {
-                // Update local.
-                final Bundle prev = mDevicePolicyLocalUserRestrictions.get(userId);
-                localChanged = !UserRestrictionsUtils.areEqual(prev, local);
-                if (localChanged) {
-                    mDevicePolicyLocalUserRestrictions.put(userId, local);
+                    mDeviceOwnerUserId = UserHandle.USER_NULL;
                 }
             }
         }
@@ -1220,12 +1224,9 @@
         }
         // Don't call them within the mRestrictionsLock.
         synchronized (mPackagesLock) {
-            if (localChanged) {
+            if (localChanged || globalChanged) {
                 writeUserLP(getUserDataNoChecks(userId));
             }
-            if (globalChanged) {
-                writeUserListLP();
-            }
         }
 
         synchronized (mRestrictionsLock) {
@@ -1237,11 +1238,30 @@
         }
     }
 
+    /**
+     * Updates restriction bundle for a given user in a given restriction array. If new bundle is
+     * empty, record is removed from the array.
+     * @return whether restrictions bundle is different from the old one.
+     */
+    private boolean updateRestrictionsIfNeededLR(int userId, @Nullable Bundle restrictions,
+            SparseArray<Bundle> restrictionsArray) {
+        final boolean changed =
+                !UserRestrictionsUtils.areEqual(restrictionsArray.get(userId), restrictions);
+        if (changed) {
+            if (!UserRestrictionsUtils.isEmpty(restrictions)) {
+                restrictionsArray.put(userId, restrictions);
+            } else {
+                restrictionsArray.delete(userId);
+            }
+        }
+        return changed;
+    }
+
     @GuardedBy("mRestrictionsLock")
     private Bundle computeEffectiveUserRestrictionsLR(int userId) {
         final Bundle baseRestrictions =
                 UserRestrictionsUtils.nonNull(mBaseUserRestrictions.get(userId));
-        final Bundle global = mDevicePolicyGlobalUserRestrictions;
+        final Bundle global = UserRestrictionsUtils.mergeAll(mDevicePolicyGlobalUserRestrictions);
         final Bundle local = mDevicePolicyLocalUserRestrictions.get(userId);
 
         if (UserRestrictionsUtils.isEmpty(global) && UserRestrictionsUtils.isEmpty(local)) {
@@ -1299,39 +1319,58 @@
      */
     @Override
     public int getUserRestrictionSource(String restrictionKey, int userId) {
-        checkManageUsersPermission("getUserRestrictionSource");
+        List<EnforcingUser> enforcingUsers = getUserRestrictionSources(restrictionKey,  userId);
+        // Get "bitwise or" of restriction sources for all enforcing users.
         int result = UserManager.RESTRICTION_NOT_SET;
+        for (int i = enforcingUsers.size() - 1; i >= 0; i--) {
+            result |= enforcingUsers.get(i).getUserRestrictionSource();
+        }
+        return result;
+    }
+
+    @Override
+    public List<EnforcingUser> getUserRestrictionSources(
+            String restrictionKey, @UserIdInt int userId) {
+        checkManageUsersPermission("getUserRestrictionSource");
 
         // Shortcut for the most common case
         if (!hasUserRestriction(restrictionKey, userId)) {
-            return result;
+            return Collections.emptyList();
         }
 
+        final List<EnforcingUser> result = new ArrayList<>();
+
+        // Check if it is base restriction.
         if (hasBaseUserRestriction(restrictionKey, userId)) {
-            result |= UserManager.RESTRICTION_SOURCE_SYSTEM;
+            result.add(new EnforcingUser(
+                    UserHandle.USER_NULL, UserManager.RESTRICTION_SOURCE_SYSTEM));
         }
 
-        synchronized(mRestrictionsLock) {
-            Bundle localRestrictions = mDevicePolicyLocalUserRestrictions.get(userId);
-            if (!UserRestrictionsUtils.isEmpty(localRestrictions)
-                    && localRestrictions.getBoolean(restrictionKey)) {
-                // Local restrictions may have been set by device owner the userId of which is
-                // stored in mGlobalRestrictionOwnerUserId.
-                if (mGlobalRestrictionOwnerUserId == userId) {
-                    result |= UserManager.RESTRICTION_SOURCE_DEVICE_OWNER;
-                } else {
-                    result |= UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
+        synchronized (mRestrictionsLock) {
+            // Check if it is set by profile owner.
+            Bundle profileOwnerRestrictions = mDevicePolicyLocalUserRestrictions.get(userId);
+            if (UserRestrictionsUtils.contains(profileOwnerRestrictions, restrictionKey)) {
+                result.add(getEnforcingUserLocked(userId));
+            }
+
+            // Iterate over all users who enforce global restrictions.
+            for (int i = mDevicePolicyGlobalUserRestrictions.size() - 1; i >= 0; i--) {
+                Bundle globalRestrictions = mDevicePolicyGlobalUserRestrictions.valueAt(i);
+                int profileUserId = mDevicePolicyGlobalUserRestrictions.keyAt(i);
+                if (UserRestrictionsUtils.contains(globalRestrictions, restrictionKey)) {
+                    result.add(getEnforcingUserLocked(profileUserId));
                 }
             }
-            if (!UserRestrictionsUtils.isEmpty(mDevicePolicyGlobalUserRestrictions)
-                    && mDevicePolicyGlobalUserRestrictions.getBoolean(restrictionKey)) {
-                result |= UserManager.RESTRICTION_SOURCE_DEVICE_OWNER;
-            }
         }
-
         return result;
     }
 
+    private EnforcingUser getEnforcingUserLocked(@UserIdInt int userId) {
+        int source = mDeviceOwnerUserId == userId ? UserManager.RESTRICTION_SOURCE_DEVICE_OWNER
+                : UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
+        return new EnforcingUser(userId, source);
+    }
+
     /**
      * @return UserRestrictions that are in effect currently.  This always returns a new
      * {@link Bundle}.
@@ -1374,28 +1413,26 @@
      * Optionally updating user restrictions, calculate the effective user restrictions and also
      * propagate to other services and system settings.
      *
-     * @param newRestrictions User restrictions to set.
+     * @param newBaseRestrictions User restrictions to set.
      *      If null, will not update user restrictions and only does the propagation.
      * @param userId target user ID.
      */
     @GuardedBy("mRestrictionsLock")
     private void updateUserRestrictionsInternalLR(
-            @Nullable Bundle newRestrictions, int userId) {
-
+            @Nullable Bundle newBaseRestrictions, int userId) {
         final Bundle prevAppliedRestrictions = UserRestrictionsUtils.nonNull(
                 mAppliedUserRestrictions.get(userId));
 
         // Update base restrictions.
-        if (newRestrictions != null) {
-            // If newRestrictions == the current one, it's probably a bug.
+        if (newBaseRestrictions != null) {
+            // If newBaseRestrictions == the current one, it's probably a bug.
             final Bundle prevBaseRestrictions = mBaseUserRestrictions.get(userId);
 
-            Preconditions.checkState(prevBaseRestrictions != newRestrictions);
+            Preconditions.checkState(prevBaseRestrictions != newBaseRestrictions);
             Preconditions.checkState(mCachedEffectiveUserRestrictions.get(userId)
-                    != newRestrictions);
+                    != newBaseRestrictions);
 
-            if (!UserRestrictionsUtils.areEqual(prevBaseRestrictions, newRestrictions)) {
-                mBaseUserRestrictions.put(userId, newRestrictions);
+            if (updateRestrictionsIfNeededLR(userId, newBaseRestrictions, mBaseUserRestrictions)) {
                 scheduleWriteUser(getUserDataNoChecks(userId));
             }
         }
@@ -1746,7 +1783,9 @@
                 }
             }
 
-            final Bundle newDevicePolicyGlobalUserRestrictions = new Bundle();
+            // Pre-O global user restriction were stored as a single bundle (as opposed to per-user
+            // currently), take care of it in case of upgrade.
+            Bundle oldDevicePolicyGlobalUserRestrictions = null;
 
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 if (type == XmlPullParser.START_TAG) {
@@ -1771,29 +1810,30 @@
                             if (type == XmlPullParser.START_TAG) {
                                 if (parser.getName().equals(TAG_RESTRICTIONS)) {
                                     synchronized (mGuestRestrictions) {
-                                        UserRestrictionsUtils
-                                                .readRestrictions(parser, mGuestRestrictions);
+                                        mGuestRestrictions.putAll(
+                                                UserRestrictionsUtils.readRestrictions(parser));
                                     }
                                 }
                                 break;
                             }
                         }
-                    } else if (name.equals(TAG_DEVICE_POLICY_RESTRICTIONS)) {
-                        UserRestrictionsUtils.readRestrictions(parser,
-                                newDevicePolicyGlobalUserRestrictions);
-                    } else if (name.equals(TAG_GLOBAL_RESTRICTION_OWNER_ID)) {
+                    } else if (name.equals(TAG_DEVICE_OWNER_USER_ID)
+                            // Legacy name, should only be encountered when upgrading from pre-O.
+                            || name.equals(TAG_GLOBAL_RESTRICTION_OWNER_ID)) {
                         String ownerUserId = parser.getAttributeValue(null, ATTR_ID);
                         if (ownerUserId != null) {
-                            mGlobalRestrictionOwnerUserId = Integer.parseInt(ownerUserId);
+                            mDeviceOwnerUserId = Integer.parseInt(ownerUserId);
                         }
+                    } else if (name.equals(TAG_DEVICE_POLICY_RESTRICTIONS)) {
+                        // Should only happen when upgrading from pre-O (version < 7).
+                        oldDevicePolicyGlobalUserRestrictions =
+                                UserRestrictionsUtils.readRestrictions(parser);
                     }
                 }
             }
-            synchronized (mRestrictionsLock) {
-                mDevicePolicyGlobalUserRestrictions = newDevicePolicyGlobalUserRestrictions;
-            }
+
             updateUserIds();
-            upgradeIfNecessaryLP();
+            upgradeIfNecessaryLP(oldDevicePolicyGlobalUserRestrictions);
         } catch (IOException | XmlPullParserException e) {
             fallbackToSingleUserLP();
         } finally {
@@ -1803,8 +1843,9 @@
 
     /**
      * Upgrade steps between versions, either for fixing bugs or changing the data format.
+     * @param oldGlobalUserRestrictions Pre-O global device policy restrictions.
      */
-    private void upgradeIfNecessaryLP() {
+    private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) {
         final int originalVersion = mUserVersion;
         int userVersion = mUserVersion;
         if (userVersion < 1) {
@@ -1855,6 +1896,23 @@
             userVersion = 6;
         }
 
+        if (userVersion < 7) {
+            // Previously only one user could enforce global restrictions, now it is per-user.
+            synchronized (mRestrictionsLock) {
+                if (!UserRestrictionsUtils.isEmpty(oldGlobalUserRestrictions)
+                        && mDeviceOwnerUserId != UserHandle.USER_NULL) {
+                    mDevicePolicyGlobalUserRestrictions.put(
+                            mDeviceOwnerUserId, oldGlobalUserRestrictions);
+                }
+                // ENSURE_VERIFY_APPS is now enforced globally even if put by profile owner, so move
+                // it from local to global bundle for all users who set it.
+                UserRestrictionsUtils.moveRestriction(UserManager.ENSURE_VERIFY_APPS,
+                        mDevicePolicyLocalUserRestrictions, mDevicePolicyGlobalUserRestrictions
+                );
+            }
+            userVersion = 7;
+        }
+
         if (userVersion < USER_VERSION) {
             Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
                     + USER_VERSION);
@@ -1893,8 +1951,10 @@
             Log.e(LOG_TAG, "Couldn't find resource: config_defaultFirstUserRestrictions", e);
         }
 
-        synchronized (mRestrictionsLock) {
-            mBaseUserRestrictions.append(UserHandle.USER_SYSTEM, restrictions);
+        if (!restrictions.isEmpty()) {
+            synchronized (mRestrictionsLock) {
+                mBaseUserRestrictions.append(UserHandle.USER_SYSTEM, restrictions);
+            }
         }
 
         updateUserIds();
@@ -2004,6 +2064,9 @@
             UserRestrictionsUtils.writeRestrictions(serializer,
                     mDevicePolicyLocalUserRestrictions.get(userInfo.id),
                     TAG_DEVICE_POLICY_RESTRICTIONS);
+            UserRestrictionsUtils.writeRestrictions(serializer,
+                    mDevicePolicyGlobalUserRestrictions.get(userInfo.id),
+                    TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS);
         }
 
         if (userData.account != null) {
@@ -2057,13 +2120,9 @@
                         .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
             }
             serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
-            synchronized (mRestrictionsLock) {
-                UserRestrictionsUtils.writeRestrictions(serializer,
-                        mDevicePolicyGlobalUserRestrictions, TAG_DEVICE_POLICY_RESTRICTIONS);
-            }
-            serializer.startTag(null, TAG_GLOBAL_RESTRICTION_OWNER_ID);
-            serializer.attribute(null, ATTR_ID, Integer.toString(mGlobalRestrictionOwnerUserId));
-            serializer.endTag(null, TAG_GLOBAL_RESTRICTION_OWNER_ID);
+            serializer.startTag(null, TAG_DEVICE_OWNER_USER_ID);
+            serializer.attribute(null, ATTR_ID, Integer.toString(mDeviceOwnerUserId));
+            serializer.endTag(null, TAG_DEVICE_OWNER_USER_ID);
             int[] userIdsToWrite;
             synchronized (mUsersLock) {
                 userIdsToWrite = new int[mUsers.size()];
@@ -2125,8 +2184,9 @@
         String seedAccountName = null;
         String seedAccountType = null;
         PersistableBundle seedAccountOptions = null;
-        Bundle baseRestrictions = new Bundle();
-        Bundle localRestrictions = new Bundle();
+        Bundle baseRestrictions = null;
+        Bundle localRestrictions = null;
+        Bundle globalRestrictions = null;
 
         XmlPullParser parser = Xml.newPullParser();
         parser.setInput(is, StandardCharsets.UTF_8.name());
@@ -2187,9 +2247,11 @@
                         name = parser.getText();
                     }
                 } else if (TAG_RESTRICTIONS.equals(tag)) {
-                    UserRestrictionsUtils.readRestrictions(parser, baseRestrictions);
+                    baseRestrictions = UserRestrictionsUtils.readRestrictions(parser);
                 } else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) {
-                    UserRestrictionsUtils.readRestrictions(parser, localRestrictions);
+                    localRestrictions = UserRestrictionsUtils.readRestrictions(parser);
+                } else if (TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS.equals(tag)) {
+                    globalRestrictions = UserRestrictionsUtils.readRestrictions(parser);
                 } else if (TAG_ACCOUNT.equals(tag)) {
                     type = parser.next();
                     if (type == XmlPullParser.TEXT) {
@@ -2224,8 +2286,15 @@
         userData.seedAccountOptions = seedAccountOptions;
 
         synchronized (mRestrictionsLock) {
-            mBaseUserRestrictions.put(id, baseRestrictions);
-            mDevicePolicyLocalUserRestrictions.put(id, localRestrictions);
+            if (baseRestrictions != null) {
+                mBaseUserRestrictions.put(id, baseRestrictions);
+            }
+            if (localRestrictions != null) {
+                mDevicePolicyLocalUserRestrictions.put(id, localRestrictions);
+            }
+            if (globalRestrictions != null) {
+                mDevicePolicyGlobalUserRestrictions.put(id, globalRestrictions);
+            }
         }
         return userData;
     }
@@ -2731,6 +2800,10 @@
             mAppliedUserRestrictions.remove(userHandle);
             mCachedEffectiveUserRestrictions.remove(userHandle);
             mDevicePolicyLocalUserRestrictions.remove(userHandle);
+            if (mDevicePolicyGlobalUserRestrictions.get(userHandle) != null) {
+                mDevicePolicyGlobalUserRestrictions.remove(userHandle);
+                applyUserRestrictionsForAllUsersLR();
+            }
         }
         // Update the user list
         synchronized (mPackagesLock) {
@@ -3420,6 +3493,9 @@
                     synchronized (mRestrictionsLock) {
                         UserRestrictionsUtils.dumpRestrictions(
                                 pw, "      ", mBaseUserRestrictions.get(userInfo.id));
+                        pw.println("    Device policy global restrictions:");
+                        UserRestrictionsUtils.dumpRestrictions(
+                                pw, "      ", mDevicePolicyGlobalUserRestrictions.get(userInfo.id));
                         pw.println("    Device policy local restrictions:");
                         UserRestrictionsUtils.dumpRestrictions(
                                 pw, "      ", mDevicePolicyLocalUserRestrictions.get(userInfo.id));
@@ -3448,13 +3524,7 @@
                 }
             }
             pw.println();
-            pw.println("  Device policy global restrictions:");
-            synchronized (mRestrictionsLock) {
-                UserRestrictionsUtils
-                        .dumpRestrictions(pw, "    ", mDevicePolicyGlobalUserRestrictions);
-            }
-            pw.println();
-            pw.println("  Global restrictions owner id:" + mGlobalRestrictionOwnerUserId);
+            pw.println("  Device owner id:" + mDeviceOwnerUserId);
             pw.println();
             pw.println("  Guest restrictions:");
             synchronized (mGuestRestrictions) {
@@ -3508,10 +3578,10 @@
 
     private class LocalService extends UserManagerInternal {
         @Override
-        public void setDevicePolicyUserRestrictions(int userId, @NonNull Bundle localRestrictions,
-                @Nullable Bundle globalRestrictions) {
-            UserManagerService.this.setDevicePolicyUserRestrictionsInner(userId, localRestrictions,
-                    globalRestrictions);
+        public void setDevicePolicyUserRestrictions(int userId, @Nullable Bundle restrictions,
+                boolean isDeviceOwner, int cameraRestrictionScope) {
+            UserManagerService.this.setDevicePolicyUserRestrictionsInner(userId, restrictions,
+                isDeviceOwner, cameraRestrictionScope);
         }
 
         @Override
@@ -3525,8 +3595,10 @@
         public void setBaseUserRestrictionsByDpmsForMigration(
                 int userId, Bundle baseRestrictions) {
             synchronized (mRestrictionsLock) {
-                mBaseUserRestrictions.put(userId, new Bundle(baseRestrictions));
-                invalidateEffectiveUserRestrictionsLR(userId);
+                if (updateRestrictionsIfNeededLR(
+                        userId, new Bundle(baseRestrictions), mBaseUserRestrictions)) {
+                    invalidateEffectiveUserRestrictionsLR(userId);
+                }
             }
 
             final UserData userData = getUserDataNoChecks(userId);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index f5b8669..d301463 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -30,11 +30,13 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlSerializer;
@@ -117,9 +119,10 @@
     );
 
     /**
-     * User restrictions that can not be set by profile owners.
+     * User restrictions that cannot be set by profile owners of secondary users. When set by DO
+     * they will be applied to all users.
      */
-    private static final Set<String> DEVICE_OWNER_ONLY_RESTRICTIONS = Sets.newArraySet(
+    private static final Set<String> PRIMARY_USER_ONLY_RESTRICTIONS = Sets.newArraySet(
             UserManager.DISALLOW_BLUETOOTH,
             UserManager.DISALLOW_USB_FILE_TRANSFER,
             UserManager.DISALLOW_CONFIG_TETHERING,
@@ -163,6 +166,13 @@
             UserManager.DISALLOW_ADD_MANAGED_PROFILE
     );
 
+    /*
+     * Special user restrictions that are always applied to all users no matter who sets them.
+     */
+    private static final Set<String> PROFILE_GLOBAL_RESTRICTIONS = Sets.newArraySet(
+            UserManager.ENSURE_VERIFY_APPS
+    );
+
     /**
      * Throws {@link IllegalArgumentException} if the given restriction name is invalid.
      */
@@ -205,6 +215,12 @@
         }
     }
 
+    public static Bundle readRestrictions(XmlPullParser parser) {
+        final Bundle result = new Bundle();
+        readRestrictions(parser, result);
+        return result;
+    }
+
     /**
      * @return {@code in} itself when it's not null, or an empty bundle (which can writable).
      */
@@ -217,6 +233,14 @@
     }
 
     /**
+     * Returns {@code true} if given bundle is not null and contains {@code true} for a given
+     * restriction.
+     */
+    public static boolean contains(@Nullable Bundle in, String restriction) {
+        return in != null && in.getBoolean(restriction);
+    }
+
+    /**
      * Creates a copy of the {@code in} Bundle.  If {@code in} is null, it'll return an empty
      * bundle.
      *
@@ -241,6 +265,22 @@
     }
 
     /**
+     * Merges a sparse array of restrictions bundles into one.
+     */
+    @Nullable
+    public static Bundle mergeAll(SparseArray<Bundle> restrictions) {
+        if (restrictions.size() == 0) {
+            return null;
+        } else {
+            final Bundle result = new Bundle();
+            for (int i = 0; i < restrictions.size(); i++) {
+                merge(result, restrictions.valueAt(i));
+            }
+            return result;
+        }
+    }
+
+    /**
      * @return true if a restriction is settable by device owner.
      */
     public static boolean canDeviceOwnerChange(String restriction) {
@@ -254,7 +294,7 @@
     public static boolean canProfileOwnerChange(String restriction, int userId) {
         return !IMMUTABLE_BY_OWNERS.contains(restriction)
                 && !(userId != UserHandle.USER_SYSTEM
-                    && DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction));
+                    && PRIMARY_USER_ONLY_RESTRICTIONS.contains(restriction));
     }
 
     /**
@@ -269,8 +309,15 @@
      * Takes restrictions that can be set by device owner, and sort them into what should be applied
      * globally and what should be applied only on the current user.
      */
-    public static void sortToGlobalAndLocal(@Nullable Bundle in, @NonNull Bundle global,
-            @NonNull Bundle local) {
+    public static void sortToGlobalAndLocal(@Nullable Bundle in, boolean isDeviceOwner,
+            int cameraRestrictionScope,
+            @NonNull Bundle global, @NonNull Bundle local) {
+        // Camera restriction (as well as all others) goes to at most one bundle.
+        if (cameraRestrictionScope == UserManagerInternal.CAMERA_DISABLED_GLOBALLY) {
+            global.putBoolean(UserManager.DISALLOW_CAMERA, true);
+        } else if (cameraRestrictionScope == UserManagerInternal.CAMERA_DISABLED_LOCALLY) {
+            local.putBoolean(UserManager.DISALLOW_CAMERA, true);
+        }
         if (in == null || in.size() == 0) {
             return;
         }
@@ -278,7 +325,7 @@
             if (!in.getBoolean(key)) {
                 continue;
             }
-            if (DEVICE_OWNER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)) {
+            if (isGlobal(isDeviceOwner, key)) {
                 global.putBoolean(key, true);
             } else {
                 local.putBoolean(key, true);
@@ -287,6 +334,15 @@
     }
 
     /**
+     * Whether given user restriction should be enforced globally.
+     */
+    private static boolean isGlobal(boolean isDeviceOwner, String key) {
+        return (isDeviceOwner &&
+                (PRIMARY_USER_ONLY_RESTRICTIONS.contains(key)|| GLOBAL_RESTRICTIONS.contains(key)))
+                || PROFILE_GLOBAL_RESTRICTIONS.contains(key);
+    }
+
+    /**
      * @return true if two Bundles contain the same user restriction.
      * A null bundle and an empty bundle are considered to be equal.
      */
@@ -485,4 +541,29 @@
             pw.println(prefix + "null");
         }
     }
+
+    /**
+     * Moves a particular restriction from one array of bundles to another, e.g. for all users.
+     */
+    public static void moveRestriction(String restrictionKey, SparseArray<Bundle> srcRestrictions,
+            SparseArray<Bundle> destRestrictions) {
+        for (int i = 0; i < srcRestrictions.size(); i++) {
+            int key = srcRestrictions.keyAt(i);
+            Bundle from = srcRestrictions.valueAt(i);
+            if (contains(from, restrictionKey)) {
+                from.remove(restrictionKey);
+                Bundle to = destRestrictions.get(key);
+                if (to == null) {
+                    to = new Bundle();
+                    destRestrictions.append(key, to);
+                }
+                to.putBoolean(restrictionKey, true);
+                // Don't keep empty bundles.
+                if (from.isEmpty()) {
+                    srcRestrictions.removeAt(i);
+                    i--;
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 7ba95a4..02b46ec 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -271,21 +271,20 @@
     }
 
     @Override
-    public void setMultiProcessEnabledFromContext(Context context) {
-        boolean enableMultiProcess = false;
-        try {
-            enableMultiProcess = Settings.Global.getInt(context.getContentResolver(),
-                    Settings.Global.WEBVIEW_MULTIPROCESS) == 1;
-        } catch (Settings.SettingNotFoundException ex) {
-        }
-        WebViewZygote.setMultiprocessEnabled(enableMultiProcess);
+    public int getMultiProcessSetting(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                                      Settings.Global.WEBVIEW_MULTIPROCESS, 0);
     }
 
     @Override
-    public void registerContentObserver(Context context, ContentObserver contentObserver) {
-        context.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.WEBVIEW_MULTIPROCESS),
-                false, contentObserver);
+    public void setMultiProcessSetting(Context context, int value) {
+        Settings.Global.putInt(context.getContentResolver(),
+                               Settings.Global.WEBVIEW_MULTIPROCESS, value);
+    }
+
+    @Override
+    public void notifyZygote(boolean enableMultiProcess) {
+        WebViewZygote.setMultiprocessEnabled(enableMultiProcess);
     }
 
     // flags declaring we want extra info from the package manager for webview providers
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 2d7a998..fd137eb 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -50,6 +50,7 @@
     public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
             throws NameNotFoundException;
 
-    public void setMultiProcessEnabledFromContext(Context context);
-    public void registerContentObserver(Context context, ContentObserver contentObserver);
+    public int getMultiProcessSetting(Context context);
+    public void setMultiProcessSetting(Context context, int value);
+    public void notifyZygote(boolean enableMultiProcess);
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 0a7454f..311570e 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -261,6 +261,32 @@
             }
         }
 
+        @Override // Binder call
+        public boolean isMultiProcessEnabled() {
+            return WebViewUpdateService.this.mImpl.isMultiProcessEnabled();
+        }
+
+        @Override // Binder call
+        public void enableMultiProcess(boolean enable) {
+            if (getContext().checkCallingPermission(
+                        android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                String msg = "Permission Denial: enableMultiProcess() from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid()
+                        + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS;
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
+
+            long callingId = Binder.clearCallingIdentity();
+            try {
+                WebViewUpdateService.this.mImpl.enableMultiProcess(enable);
+            } finally {
+                Binder.restoreCallingIdentity(callingId);
+            }
+        }
+
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 1a77c68..edfb11c 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -20,8 +20,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
-import android.database.ContentObserver;
-import android.net.Uri;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.util.Base64;
@@ -77,7 +75,6 @@
 
     private SystemInterface mSystemInterface;
     private WebViewUpdater mWebViewUpdater;
-    private SettingsObserver mSettingsObserver;
     final private Context mContext;
 
     public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
@@ -97,10 +94,7 @@
     void prepareWebViewInSystemServer() {
         updateFallbackStateOnBoot();
         mWebViewUpdater.prepareWebViewInSystemServer();
-
-        // Register for changes in the multiprocess developer option. This has to be done
-        // here, since the update service gets created before the ContentResolver service.
-        mSettingsObserver = new SettingsObserver();
+        mSystemInterface.notifyZygote(isMultiProcessEnabled());
     }
 
     private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
@@ -247,6 +241,19 @@
                 && packageName.equals(fallbackProvider.packageName));
     }
 
+    boolean isMultiProcessEnabled() {
+        return mSystemInterface.getMultiProcessSetting(mContext) != 0;
+    }
+
+    void enableMultiProcess(boolean enable) {
+        PackageInfo current = getCurrentWebViewPackage();
+        mSystemInterface.setMultiProcessSetting(mContext, enable ? 1 : 0);
+        mSystemInterface.notifyZygote(enable);
+        if (current != null) {
+            mSystemInterface.killPackageDependents(current.packageName);
+        }
+    }
+
     /**
      * Class that decides what WebView implementation to use and prepares that implementation for
      * use.
@@ -740,31 +747,6 @@
     }
 
     /**
-     * Watches for changes in the WEBVIEW_MULTIPROCESS setting and lets
-     * the WebViewZygote know, so it can start or stop the zygote process
-     * appropriately.
-     */
-    private class SettingsObserver extends ContentObserver {
-        SettingsObserver() {
-            super(new Handler());
-
-            mSystemInterface.registerContentObserver(mContext, this);
-
-            // Push the current value of the setting immediately.
-            notifyZygote();
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            notifyZygote();
-        }
-
-        private void notifyZygote() {
-            mSystemInterface.setMultiProcessEnabledFromContext(mContext);
-        }
-    }
-
-    /**
      * Dump the state of this Service.
      */
     void dumpState(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 10d1d8b..ac9859d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -445,6 +445,7 @@
 
         mService.mOpeningApps.remove(this);
         mService.mUnknownAppVisibilityController.appRemoved(this);
+        mService.mTaskSnapshotController.onAppRemoved(this);
         waitingToShow = false;
         if (mService.mClosingApps.contains(this)) {
             delayed = true;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2d50e3a..7325daab 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -637,6 +637,10 @@
         return mFillsParent || !StackId.isTaskResizeAllowed(mStack.mStackId);
     }
 
+    TaskWindowContainerController getController() {
+        return (TaskWindowContainerController) super.getController();
+    }
+
     @Override
     public String toString() {
         return "{taskId=" + mTaskId + " appTokens=" + mChildren + " mdr=" + mDeferRemoval + "}";
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index 994a155..c86229b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -20,6 +20,8 @@
 import android.app.ActivityManager.TaskSnapshot;
 import android.util.ArrayMap;
 
+import java.io.PrintWriter;
+
 /**
  * Caches snapshots. See {@link TaskSnapshotController}.
  * <p>
@@ -27,13 +29,65 @@
  */
 class TaskSnapshotCache {
 
-    private final ArrayMap<Task, TaskSnapshot> mCache = new ArrayMap<>();
+    private final ArrayMap<AppWindowToken, Task> mAppTaskMap = new ArrayMap<>();
+    private final ArrayMap<Task, CacheEntry> mCache = new ArrayMap<>();
 
     void putSnapshot(Task task, TaskSnapshot snapshot) {
-        mCache.put(task, snapshot);
+        final CacheEntry entry = mCache.get(task);
+        if (entry != null) {
+            mAppTaskMap.remove(entry.topApp);
+        }
+        final AppWindowToken top = task.getTopChild();
+        mAppTaskMap.put(top, task);
+        mCache.put(task, new CacheEntry(snapshot, task.getTopChild()));
     }
 
     @Nullable TaskSnapshot getSnapshot(Task task) {
-        return mCache.get(task);
+        final CacheEntry entry = mCache.get(task);
+        return entry != null ? entry.snapshot : null;
+    }
+
+    /**
+     * Cleans the cache after an app window token's process died.
+     */
+    void cleanCache(AppWindowToken wtoken) {
+        final Task task = mAppTaskMap.get(wtoken);
+        if (task != null) {
+            removeEntry(task);
+        }
+    }
+
+    private void removeEntry(Task task) {
+        final CacheEntry entry = mCache.get(task);
+        if (entry != null) {
+            mAppTaskMap.remove(entry.topApp);
+            mCache.remove(task);
+        }
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        final String doublePrefix = prefix + "  ";
+        final String triplePrefix = doublePrefix + "  ";
+        pw.println(prefix + "SnapshotCache");
+        for (int i = mCache.size() - 1; i >= 0; i--) {
+            final CacheEntry entry = mCache.valueAt(i);
+            pw.println(doublePrefix + "Entry taskId=" + mCache.keyAt(i).mTaskId);
+            pw.println(triplePrefix + "topApp=" + entry.topApp);
+            pw.println(triplePrefix + "snapshot=" + entry.snapshot);
+        }
+    }
+
+    private static final class CacheEntry {
+
+        /** The snapshot. */
+        final TaskSnapshot snapshot;
+
+        /** The app token that was on top of the task when the snapshot was taken */
+        final AppWindowToken topApp;
+
+        CacheEntry(TaskSnapshot snapshot, AppWindowToken topApp) {
+            this.snapshot = snapshot;
+            this.topApp = topApp;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 68aceae..df8679d 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -17,23 +17,18 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS;
-import static android.graphics.Bitmap.Config.ARGB_8888;
-import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE;
-import static android.graphics.GraphicBuffer.USAGE_SW_READ_NEVER;
-import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_NEVER;
-import static android.graphics.PixelFormat.RGBA_8888;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager.StackId;
 import android.app.ActivityManager.TaskSnapshot;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.GraphicBuffer;
 import android.util.ArraySet;
 import android.view.WindowManagerPolicy.StartingSurface;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.PrintWriter;
+
 /**
  * When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and
  * put it into our cache. Internally we use gralloc buffers to be able to draw them wherever we
@@ -74,6 +69,9 @@
             final TaskSnapshot snapshot = snapshotTask(task);
             if (snapshot != null) {
                 mCache.putSnapshot(task, snapshot);
+                if (task.getController() != null) {
+                    task.getController().reportSnapshotChanged(snapshot);
+                }
             }
         }
     }
@@ -92,7 +90,7 @@
     }
 
     private TaskSnapshot snapshotTask(Task task) {
-        final AppWindowToken top = (AppWindowToken) task.getTop();
+        final AppWindowToken top = task.getTopChild();
         if (top == null) {
             return null;
         }
@@ -125,4 +123,25 @@
     private boolean canSnapshotTask(Task task) {
         return !StackId.isHomeOrRecentsStack(task.mStack.mStackId);
     }
+
+    /**
+     * Called when an {@link AppWindowToken} has been removed.
+     */
+    void onAppRemoved(AppWindowToken wtoken) {
+        // TODO: Clean from both recents and running cache.
+        mCache.cleanCache(wtoken);
+    }
+
+    /**
+     * Called when the process of an {@link AppWindowToken} has died.
+     */
+    void onAppDied(AppWindowToken wtoken) {
+
+        // TODO: Only clean from running cache.
+        mCache.cleanCache(wtoken);
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        mCache.dump(pw, prefix);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index bbc9ed2..0ca1887 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -19,6 +19,9 @@
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.util.EventLog;
 import android.util.Slog;
 import com.android.internal.annotations.VisibleForTesting;
@@ -37,14 +40,28 @@
  * Test class: {@link TaskWindowContainerControllerTests}
  */
 public class TaskWindowContainerController
-        extends WindowContainerController<Task, WindowContainerListener> {
+        extends WindowContainerController<Task, TaskWindowContainerListener> {
+
+    private static final int REPORT_SNAPSHOT_CHANGED = 0;
 
     private final int mTaskId;
 
-    public TaskWindowContainerController(int taskId, int stackId, int userId, Rect bounds,
-            Configuration overrideConfig, int resizeMode, boolean homeTask, boolean isOnTopLauncher,
-            boolean toTop, boolean showForAllUsers) {
-        super(null, WindowManagerService.getInstance());
+    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case REPORT_SNAPSHOT_CHANGED:
+                    mListener.onSnapshotChanged((TaskSnapshot) msg.obj);
+                    break;
+            }
+        }
+    };
+
+    public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
+            int stackId, int userId, Rect bounds, Configuration overrideConfig, int resizeMode,
+            boolean homeTask, boolean isOnTopLauncher, boolean toTop, boolean showForAllUsers) {
+        super(listener, WindowManagerService.getInstance());
         mTaskId = taskId;
 
         synchronized(mWindowMap) {
@@ -259,6 +276,10 @@
         }
     }
 
+    void reportSnapshotChanged(TaskSnapshot snapshot) {
+        mHandler.obtainMessage(REPORT_SNAPSHOT_CHANGED, snapshot).sendToTarget();
+    }
+
     @Override
     public String toString() {
         return "{TaskWindowContainerController taskId=" + mTaskId + "}";
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerListener.java b/services/core/java/com/android/server/wm/TaskWindowContainerListener.java
new file mode 100644
index 0000000..61b202d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.app.ActivityManager.TaskSnapshot;
+
+/**
+ * Interface used by the creator of the controller to listen to changes with the container.
+ */
+public interface TaskWindowContainerListener extends WindowContainerListener {
+
+    /**
+     * Called when the snapshot of this task has changed.
+     */
+    void onSnapshotChanged(TaskSnapshot snapshot);
+}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e231da8..5b96263 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -458,9 +458,9 @@
         return false;
     }
 
-    /** Returns the top child container or this container if there are no children. */
-    WindowContainer getTop() {
-        return mChildren.isEmpty() ? this : mChildren.peekLast();
+    /** Returns the top child container. */
+    E getTopChild() {
+        return mChildren.peekLast();
     }
 
     /** Returns true if there is still a removal being deferred */
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0c8c10b..dcc0c6f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -235,6 +235,8 @@
 import java.util.HashMap;
 import java.util.List;
 
+import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+import static android.Manifest.permission.READ_FRAME_BUFFER;
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
         implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
@@ -3859,7 +3861,7 @@
 
     @Override
     public Bitmap screenshotWallpaper() {
-        if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER,
+        if (!checkCallingPermission(READ_FRAME_BUFFER,
                 "screenshotWallpaper()")) {
             throw new SecurityException("Requires READ_FRAME_BUFFER permission");
         }
@@ -3880,7 +3882,7 @@
      */
     @Override
     public boolean requestAssistScreenshot(final IAssistScreenshotReceiver receiver) {
-        if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER,
+        if (!checkCallingPermission(READ_FRAME_BUFFER,
                 "requestAssistScreenshot()")) {
             throw new SecurityException("Requires READ_FRAME_BUFFER permission");
         }
@@ -7148,6 +7150,7 @@
 
         mInputMonitor.dump(pw, "  ");
         mUnknownAppVisibilityController.dump(pw, "  ");
+        mTaskSnapshotController.dump(pw, "  ");
 
         if (dumpAll) {
             pw.print("  mSystemDecorLayer="); pw.print(mSystemDecorLayer);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b1bf2c6..10aebe6 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2304,6 +2304,9 @@
                     final WindowState win = mService.windowForClientLocked(mSession, mClient, false);
                     Slog.i(TAG, "WIN DEATH: " + win);
                     if (win != null) {
+                        if (win.mAppToken != null && win.mAppToken.findMainWindow() == win) {
+                            mService.mTaskSnapshotController.onAppDied(win.mAppToken);
+                        }
                         win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
                         if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
                             // The owner of the docked divider died :( We reset the docked stack,
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 27efd05..6791da9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1462,7 +1462,11 @@
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
     PointerIcon pointerIcon;
-    android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon);
+    status_t result = android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon);
+    if (result) {
+        jniThrowRuntimeException(env, "Failed to load custom pointer icon.");
+        return;
+    }
 
     SpriteIcon spriteIcon;
     pointerIcon.bitmap.copyTo(&spriteIcon.bitmap, kN32_SkColorType);
diff --git a/services/core/jni/com_android_server_location_ContextHubService.cpp b/services/core/jni/com_android_server_location_ContextHubService.cpp
index 90b9207..517fce0 100644
--- a/services/core/jni/com_android_server_location_ContextHubService.cpp
+++ b/services/core/jni/com_android_server_location_ContextHubService.cpp
@@ -25,11 +25,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/endian.h>
 
 #include <chrono>
 #include <mutex>
 #include <queue>
 #include <unordered_map>
+#include <utility>
 
 #include <android-base/macros.h>
 #include <android/hardware/contexthub/1.0/IContexthub.h>
@@ -44,6 +46,7 @@
 using android::hardware::contexthub::V1_0::HubAppInfo;
 using android::hardware::contexthub::V1_0::IContexthub;
 using android::hardware::contexthub::V1_0::IContexthubCallback;
+using android::hardware::contexthub::V1_0::NanoAppBinary;
 using android::hardware::contexthub::V1_0::Result;
 using android::hardware::contexthub::V1_0::TransactionResult;
 
@@ -61,6 +64,22 @@
 
 namespace android {
 
+constexpr uint32_t kNanoAppBinaryHeaderVersion = 1;
+
+// Important: this header is explicitly defined as little endian byte order, and
+// therefore may not match host endianness
+struct NanoAppBinaryHeader {
+    uint32_t headerVersion;        // 0x1 for this version
+    uint32_t magic;                // "NANO" (see NANOAPP_MAGIC in context_hub.h)
+    uint64_t appId;                // App Id, contains vendor id
+    uint32_t appVersion;           // Version of the app
+    uint32_t flags;                // Signed, encrypted
+    uint64_t hwHubType;            // Which hub type is this compiled for
+    uint8_t targetChreApiMajorVersion; // Which CHRE API version this is compiled for
+    uint8_t targetChreApiMinorVersion;
+    uint8_t reserved[6];
+} __attribute__((packed));
+
 enum HubMessageType {
     CONTEXT_HUB_APPS_ENABLE  = 1, // Enables loaded nano-app(s)
     CONTEXT_HUB_APPS_DISABLE = 2, // Disables loaded nano-app(s)
@@ -989,6 +1008,45 @@
     return retArray;
 }
 
+Result sendLoadNanoAppRequest(uint32_t hubId,
+                              jbyte *data,
+                              size_t dataBufferLength) {
+    auto header = reinterpret_cast<const NanoAppBinaryHeader *>(data);
+    Result result;
+
+    if (dataBufferLength < sizeof(NanoAppBinaryHeader)) {
+        ALOGE("Got short NanoApp, length %zu", dataBufferLength);
+        result = Result::BAD_PARAMS;
+    } else if (header->headerVersion != htole32(kNanoAppBinaryHeaderVersion)) {
+        ALOGE("Got unexpected NanoApp header version %" PRIu32,
+              letoh32(header->headerVersion));
+        result = Result::BAD_PARAMS;
+    } else {
+        NanoAppBinary nanoapp;
+
+        // Data from the common nanoapp header goes into explicit fields
+        nanoapp.appId      = letoh64(header->appId);
+        nanoapp.appVersion = letoh32(header->appVersion);
+        nanoapp.flags      = letoh32(header->flags);
+        nanoapp.targetChreApiMajorVersion = header->targetChreApiMajorVersion;
+        nanoapp.targetChreApiMinorVersion = header->targetChreApiMinorVersion;
+
+        // Everything past the header goes in customBinary
+        auto dataBytes = reinterpret_cast<const uint8_t *>(data);
+        std::vector<uint8_t> customBinary(
+            dataBytes + sizeof(NanoAppBinaryHeader),
+            dataBytes + dataBufferLength);
+        nanoapp.customBinary = std::move(customBinary);
+
+        ALOGW("Calling Load NanoApp on hub %d", hubId);
+        result = db.hubInfo.contextHub->loadNanoApp(hubId,
+                                                    nanoapp,
+                                                    CONTEXT_HUB_LOAD_APP);
+    }
+
+    return result;
+}
+
 jint nativeSendMessage(JNIEnv *env,
                        jobject instance,
                        jintArray header_,
@@ -1068,12 +1126,7 @@
     } else {
         if (appInstanceHandle == OS_APP_ID) {
             if (msgType == CONTEXT_HUB_LOAD_APP) {
-                std::vector<uint8_t> dataVector(reinterpret_cast<uint8_t *>(data),
-                                                reinterpret_cast<uint8_t *>(data + dataBufferLength));
-                ALOGW("Calling Load NanoApp on hub %d", hubId);
-                result = db.hubInfo.contextHub->loadNanoApp(hubId,
-                                                            dataVector,
-                                                            CONTEXT_HUB_LOAD_APP);
+                result = sendLoadNanoAppRequest(hubId, data, dataBufferLength);
             } else {
                 ALOGD("Dropping OS addresses message of type - %" PRIu32, msgType);
                 result = Result::BAD_PARAMS;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8d8934e..111f37f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -191,6 +191,7 @@
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -235,7 +236,7 @@
 
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
-    private static final long MS_PER_DAY = 86400 * 1000;
+    private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
 
     private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms
 
@@ -330,7 +331,7 @@
      * Minimum timeout in milliseconds after which unlocking with weak auth times out,
      * i.e. the user has to use a strong authentication method like password, PIN or pattern.
      */
-    private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
+    private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = TimeUnit.HOURS.toMillis(1);
 
     /**
      * Strings logged with {@link
@@ -1154,7 +1155,7 @@
                 } else if (TAG_KEEP_UNINSTALLED_PACKAGES.equals(tag)) {
                     keepUninstalledPackages = readPackageList(parser, tag);
                 } else if (TAG_USER_RESTRICTIONS.equals(tag)) {
-                    UserRestrictionsUtils.readRestrictions(parser, ensureUserRestrictions());
+                    userRestrictions = UserRestrictionsUtils.readRestrictions(parser);
                 } else if (TAG_DEFAULT_ENABLED_USER_RESTRICTIONS.equals(tag)) {
                     readAttributeValues(
                             parser, TAG_RESTRICTION, defaultEnabledRestrictionsAlreadySet);
@@ -7779,7 +7780,7 @@
 
         final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
-            ActiveAdmin activeAdmin =
+            final ActiveAdmin activeAdmin =
                     getActiveAdminForCallerLocked(who,
                             DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             final boolean isDeviceOwner = isDeviceOwner(who, userHandle);
@@ -7794,7 +7795,12 @@
             }
 
             // Save the restriction to ActiveAdmin.
-            activeAdmin.ensureUserRestrictions().putBoolean(key, enabledFromThisOwner);
+            final Bundle restrictions = activeAdmin.ensureUserRestrictions();
+            if (enabledFromThisOwner) {
+                restrictions.putBoolean(key, true);
+            } else {
+                restrictions.remove(key);
+            }
             saveUserRestrictionsLocked(userHandle);
         }
     }
@@ -7807,39 +7813,46 @@
 
     private void pushUserRestrictions(int userId) {
         synchronized (this) {
-            final Bundle global;
-            final Bundle local = new Bundle();
-            if (mOwners.isDeviceOwnerUserId(userId)) {
-                global = new Bundle();
+            final boolean isDeviceOwner = mOwners.isDeviceOwnerUserId(userId);
+            final Bundle userRestrictions;
+            // Whether device owner enforces camera restriction.
+            boolean disallowCameraGlobally = false;
 
+            if (isDeviceOwner) {
                 final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
                 if (deviceOwner == null) {
                     return; // Shouldn't happen.
                 }
-
-                UserRestrictionsUtils.sortToGlobalAndLocal(deviceOwner.userRestrictions,
-                        global, local);
+                userRestrictions = deviceOwner.userRestrictions;
                 // DO can disable camera globally.
-                if (deviceOwner.disableCamera) {
-                    global.putBoolean(UserManager.DISALLOW_CAMERA, true);
-                }
+                disallowCameraGlobally = deviceOwner.disableCamera;
             } else {
-                global = null;
+                final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
+                userRestrictions = profileOwner != null ? profileOwner.userRestrictions : null;
+            }
 
-                ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
-                if (profileOwner != null) {
-                    UserRestrictionsUtils.merge(local, profileOwner.userRestrictions);
-                }
-            }
-            // Also merge in *local* camera restriction.
-            if (getCameraDisabled(/* who= */ null,
-                    userId, /* mergeDeviceOwnerRestriction= */ false)) {
-                local.putBoolean(UserManager.DISALLOW_CAMERA, true);
-            }
-            mUserManagerInternal.setDevicePolicyUserRestrictions(userId, local, global);
+            // Whether any admin enforces camera restriction.
+            final int cameraRestrictionScope =
+                    getCameraRestrictionScopeLocked(userId, disallowCameraGlobally);
+
+            mUserManagerInternal.setDevicePolicyUserRestrictions(userId, userRestrictions,
+                    isDeviceOwner, cameraRestrictionScope);
         }
     }
 
+    /**
+     * Get the scope of camera restriction for a given user if any.
+     */
+    private int getCameraRestrictionScopeLocked(int userId, boolean disallowCameraGlobally) {
+        if (disallowCameraGlobally) {
+            return UserManagerInternal.CAMERA_DISABLED_GLOBALLY;
+        } else if (getCameraDisabled(
+                /* who= */ null, userId, /* mergeDeviceOwnerRestriction= */ false)) {
+            return UserManagerInternal.CAMERA_DISABLED_LOCALLY;
+        }
+        return UserManagerInternal.CAMERA_NOT_DISABLED;
+    }
+
     @Override
     public Bundle getUserRestrictions(ComponentName who) {
         if (!mHasFeature) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 08fb591..157ea85 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -955,6 +955,21 @@
                 }
                 traceEnd();
 
+                // Wifi Service must be started first for wifi-related services.
+                traceBeginAndSlog("StartWifi");
+                mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+                traceEnd();
+                traceBeginAndSlog("StartWifiScanning");
+                mSystemServiceManager.startService(
+                        "com.android.server.wifi.scanner.WifiScanningService");
+                traceEnd();
+
+                if (!disableRtt) {
+                    traceBeginAndSlog("StartWifiRtt");
+                    mSystemServiceManager.startService("com.android.server.wifi.RttService");
+                    traceEnd();
+                }
+
                 if (context.getPackageManager().hasSystemFeature(
                         PackageManager.FEATURE_WIFI_AWARE)) {
                     traceBeginAndSlog("StartWifiAware");
@@ -970,19 +985,6 @@
                     mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
                     traceEnd();
                 }
-                traceBeginAndSlog("StartWifi");
-                mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
-                traceEnd();
-                traceBeginAndSlog("StartWifiScanning");
-                mSystemServiceManager.startService(
-                            "com.android.server.wifi.scanner.WifiScanningService");
-                traceEnd();
-
-                if (!disableRtt) {
-                    traceBeginAndSlog("StartWifiRtt");
-                    mSystemServiceManager.startService("com.android.server.wifi.RttService");
-                    traceEnd();
-                }
 
                 if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
                     mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index f1eaf9b..8da47c8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -15,6 +15,10 @@
  */
 package com.android.server.devicepolicy;
 
+import static android.os.UserManagerInternal.CAMERA_DISABLED_GLOBALLY;
+import static android.os.UserManagerInternal.CAMERA_DISABLED_LOCALLY;
+import static android.os.UserManagerInternal.CAMERA_NOT_DISABLED;
+
 import android.Manifest.permission;
 import android.app.Activity;
 import android.app.admin.DeviceAdminReceiver;
@@ -39,6 +43,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.test.MoreAsserts;
@@ -61,9 +66,11 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
@@ -928,9 +935,8 @@
 
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(),
-                MockUtils.checkUserRestrictions()
-        );
+                eq(null),
+                eq(true), eq(CAMERA_NOT_DISABLED));
 
         assertFalse(dpm.isAdminActiveAsUser(admin1, UserHandle.USER_SYSTEM));
 
@@ -1287,7 +1293,8 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(defaultRestrictions),
-                MockUtils.checkUserRestrictions()
+                eq(true) /* isDeviceOwner */,
+                eq(CAMERA_NOT_DISABLED)
         );
         reset(mContext.userManagerInternal);
 
@@ -1296,21 +1303,21 @@
         }
 
         assertNoDeviceOwnerRestrictions();
+        reset(mContext.userManagerInternal);
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER)
-        );
+                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER),
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER)
-        );
+                MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS,
+                        UserManager.DISALLOW_ADD_USER),
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         DpmTestUtils.assertRestrictions(
@@ -1328,8 +1335,7 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
-                MockUtils.checkUserRestrictions()
-        );
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         DpmTestUtils.assertRestrictions(
@@ -1345,8 +1351,7 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(),
-                MockUtils.checkUserRestrictions()
-        );
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         assertNoDeviceOwnerRestrictions();
@@ -1358,42 +1363,38 @@
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_UNMUTE_MICROPHONE);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
-                        UserManager.DISALLOW_UNMUTE_MICROPHONE)
-        );
+                        UserManager.DISALLOW_UNMUTE_MICROPHONE),
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         dpm.clearUserRestriction(admin1, UserManager.DISALLOW_ADJUST_VOLUME);
         dpm.clearUserRestriction(admin1, UserManager.DISALLOW_UNMUTE_MICROPHONE);
-
+        reset(mContext.userManagerInternal);
 
         // More tests.
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER)
-        );
+                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER),
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_FUN);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
-                        UserManager.DISALLOW_ADD_USER)
-        );
+                        UserManager.DISALLOW_ADD_USER),
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         dpm.setCameraDisabled(admin1, true);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 // DISALLOW_CAMERA will be applied to both local and global.
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
-                        UserManager.DISALLOW_CAMERA, UserManager.DISALLOW_ADD_USER)
-        );
+                        UserManager.DISALLOW_ADD_USER),
+                eq(true), eq(CAMERA_DISABLED_GLOBALLY));
         reset(mContext.userManagerInternal);
 
         // Set up another DA and let it disable camera.  Now DISALLOW_CAMERA will only be applied
@@ -1407,11 +1408,10 @@
 
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                // DISALLOW_CAMERA will be applied to both local and global.
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA),
+                // DISALLOW_CAMERA will be applied to both local and global. <- TODO: fix this
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
-                        UserManager.DISALLOW_ADD_USER)
-        );
+                        UserManager.DISALLOW_ADD_USER),
+                eq(true), eq(CAMERA_DISABLED_LOCALLY));
         reset(mContext.userManagerInternal);
         // TODO Make sure restrictions are written to the file.
     }
@@ -1429,8 +1429,7 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES),
-                isNull(Bundle.class)
-        );
+                eq(false), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
@@ -1438,8 +1437,7 @@
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
                         UserManager.DISALLOW_OUTGOING_CALLS),
-                isNull(Bundle.class)
-        );
+                eq(false), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         DpmTestUtils.assertRestrictions(
@@ -1462,8 +1460,7 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
-                isNull(Bundle.class)
-        );
+                eq(false), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         DpmTestUtils.assertRestrictions(
@@ -1484,8 +1481,7 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(),
-                isNull(Bundle.class)
-        );
+                eq(false), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         DpmTestUtils.assertRestrictions(
@@ -1507,18 +1503,15 @@
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
                         UserManager.DISALLOW_UNMUTE_MICROPHONE),
-                isNull(Bundle.class)
-        );
+                eq(false), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         dpm.setCameraDisabled(admin1, true);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA,
-                        UserManager.DISALLOW_ADJUST_VOLUME,
+                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
                         UserManager.DISALLOW_UNMUTE_MICROPHONE),
-                isNull(Bundle.class)
-        );
+                eq(false), eq(CAMERA_DISABLED_LOCALLY));
         reset(mContext.userManagerInternal);
 
         // TODO Make sure restrictions are written to the file.
@@ -1558,7 +1551,8 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(defaultRestrictions),
-                MockUtils.checkUserRestrictions()
+                eq(true) /* isDeviceOwner */,
+                eq(CAMERA_NOT_DISABLED)
         );
         reset(mContext.userManagerInternal);
 
@@ -1600,7 +1594,8 @@
             verify(mContext.userManagerInternal, atLeast(1)).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(newDefaultEnabledRestriction),
-                MockUtils.checkUserRestrictions()
+                eq(true) /* isDeviceOwner */,
+                eq(CAMERA_NOT_DISABLED)
             );
             reset(mContext.userManagerInternal);
 
@@ -2124,8 +2119,37 @@
         setupDeviceOwner();
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
-        final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
-        final long ONE_MINUTE = 60 * 1000;
+        final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = TimeUnit.HOURS.toMillis(1);
+        final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1);
+        final long MIN_PLUS_ONE_MINUTE = MINIMUM_STRONG_AUTH_TIMEOUT_MS + ONE_MINUTE;
+        final long MAX_MINUS_ONE_MINUTE = DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS
+                - ONE_MINUTE;
+
+        // verify that the minimum timeout cannot be modified on user builds (system property is
+        // not being read)
+        mContext.buildMock.isDebuggable = false;
+
+        dpm.setRequiredStrongAuthTimeout(admin1, MAX_MINUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MAX_MINUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(null), MAX_MINUS_ONE_MINUTE);
+
+        verify(mContext.systemProperties, never()).getLong(anyString(), anyLong());
+
+        // restore to the debuggable build state
+        mContext.buildMock.isDebuggable = true;
+
+        // Always return the default (second arg) when getting system property for long type
+        when(mContext.systemProperties.getLong(anyString(), anyLong())).thenAnswer(
+                new Answer<Long>() {
+                    @Override
+                    public Long answer(InvocationOnMock invocation) throws Throwable {
+                        return (Long) invocation.getArguments()[1];
+                    }
+                }
+        );
+
+        // reset to default (0 means the admin is not participating, so default should be returned)
+        dpm.setRequiredStrongAuthTimeout(admin1, 0);
 
         // aggregation should be the default if unset by any admin
         assertEquals(dpm.getRequiredStrongAuthTimeout(null),
@@ -2142,7 +2166,7 @@
         assertEquals(dpm.getRequiredStrongAuthTimeout(null),
                 DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS);
 
-        // 0 means default
+        // 0 means the admin is not participating, so default should be returned
         dpm.setRequiredStrongAuthTimeout(admin1, 0);
         assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), 0);
         assertEquals(dpm.getRequiredStrongAuthTimeout(null),
@@ -2153,12 +2177,14 @@
         assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MINIMUM_STRONG_AUTH_TIMEOUT_MS);
         assertEquals(dpm.getRequiredStrongAuthTimeout(null), MINIMUM_STRONG_AUTH_TIMEOUT_MS);
 
-        // value within range
-        dpm.setRequiredStrongAuthTimeout(admin1, MINIMUM_STRONG_AUTH_TIMEOUT_MS + ONE_MINUTE);
-        assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MINIMUM_STRONG_AUTH_TIMEOUT_MS
-                + ONE_MINUTE);
-        assertEquals(dpm.getRequiredStrongAuthTimeout(null), MINIMUM_STRONG_AUTH_TIMEOUT_MS
-                + ONE_MINUTE);
+        // values within range
+        dpm.setRequiredStrongAuthTimeout(admin1, MIN_PLUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MIN_PLUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(null), MIN_PLUS_ONE_MINUTE);
+
+        dpm.setRequiredStrongAuthTimeout(admin1, MAX_MINUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MAX_MINUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(null), MAX_MINUS_ONE_MINUTE);
 
         // reset to default
         dpm.setRequiredStrongAuthTimeout(admin1, 0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index 11f9ebb..480be2e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -16,13 +16,16 @@
 
 package com.android.server.pm;
 
+import static com.android.server.devicepolicy.DpmTestUtils.assertRestrictions;
+import static com.android.server.devicepolicy.DpmTestUtils.newRestrictions;
+
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.server.devicepolicy.DpmTestUtils;
+import android.util.SparseArray;
 
 /**
  * Tests for {@link com.android.server.pm.UserRestrictionsUtils}.
@@ -49,14 +52,14 @@
     public void testIsEmpty() {
         assertTrue(UserRestrictionsUtils.isEmpty(null));
         assertTrue(UserRestrictionsUtils.isEmpty(new Bundle()));
-        assertFalse(UserRestrictionsUtils.isEmpty(DpmTestUtils.newRestrictions("a")));
+        assertFalse(UserRestrictionsUtils.isEmpty(newRestrictions("a")));
     }
 
     public void testClone() {
         Bundle in = new Bundle();
         Bundle out = UserRestrictionsUtils.clone(in);
         assertNotSame(in, out);
-        DpmTestUtils.assertRestrictions(out, new Bundle());
+        assertRestrictions(out, new Bundle());
 
         out = UserRestrictionsUtils.clone(null);
         assertNotNull(out);
@@ -64,16 +67,16 @@
     }
 
     public void testMerge() {
-        Bundle a = DpmTestUtils.newRestrictions("a", "d");
-        Bundle b = DpmTestUtils.newRestrictions("b", "d", "e");
+        Bundle a = newRestrictions("a", "d");
+        Bundle b = newRestrictions("b", "d", "e");
 
         UserRestrictionsUtils.merge(a, b);
 
-        DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions("a", "b", "d", "e"), a);
+        assertRestrictions(newRestrictions("a", "b", "d", "e"), a);
 
         UserRestrictionsUtils.merge(a, null);
 
-        DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions("a", "b", "d", "e"), a);
+        assertRestrictions(newRestrictions("a", "b", "d", "e"), a);
 
         try {
             UserRestrictionsUtils.merge(a, a);
@@ -114,25 +117,32 @@
         final Bundle local = new Bundle();
         final Bundle global = new Bundle();
 
-        UserRestrictionsUtils.sortToGlobalAndLocal(null, global, local);
+        UserRestrictionsUtils.sortToGlobalAndLocal(null, false /* isDeviceOwner */,
+                UserManagerInternal.CAMERA_NOT_DISABLED, global, local);
         assertEquals(0, global.size());
         assertEquals(0, local.size());
 
-        UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, global, local);
+        UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, false /* isDeviceOwner */,
+                UserManagerInternal.CAMERA_NOT_DISABLED, global, local);
         assertEquals(0, global.size());
         assertEquals(0, local.size());
 
-        UserRestrictionsUtils.sortToGlobalAndLocal(DpmTestUtils.newRestrictions(
+        // Restrictions set by DO.
+        UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(
                 UserManager.DISALLOW_ADJUST_VOLUME,
                 UserManager.DISALLOW_UNMUTE_MICROPHONE,
                 UserManager.DISALLOW_USB_FILE_TRANSFER,
                 UserManager.DISALLOW_CONFIG_TETHERING,
                 UserManager.DISALLOW_OUTGOING_BEAM,
-                UserManager.DISALLOW_APPS_CONTROL
-        ), global, local);
+                UserManager.DISALLOW_APPS_CONTROL,
+                UserManager.ENSURE_VERIFY_APPS
+        ), true /* isDeviceOwner */, UserManagerInternal.CAMERA_NOT_DISABLED, global, local);
 
 
-        DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions(
+        assertRestrictions(newRestrictions(
+                // This one is global no matter who sets it.
+                UserManager.ENSURE_VERIFY_APPS,
+
                 // These can be set by PO too, but when DO sets them, they're global.
                 UserManager.DISALLOW_ADJUST_VOLUME,
                 UserManager.DISALLOW_UNMUTE_MICROPHONE,
@@ -142,11 +152,117 @@
                 UserManager.DISALLOW_CONFIG_TETHERING
         ), global);
 
-        DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions(
+        assertRestrictions(newRestrictions(
                 // They can be set by both DO/PO.
                 UserManager.DISALLOW_OUTGOING_BEAM,
                 UserManager.DISALLOW_APPS_CONTROL
         ), local);
+
+        local.clear();
+        global.clear();
+
+        // Restrictions set by PO.
+        UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(
+                UserManager.DISALLOW_ADJUST_VOLUME,
+                UserManager.DISALLOW_UNMUTE_MICROPHONE,
+                UserManager.DISALLOW_USB_FILE_TRANSFER,
+                UserManager.DISALLOW_CONFIG_TETHERING,
+                UserManager.DISALLOW_OUTGOING_BEAM,
+                UserManager.DISALLOW_APPS_CONTROL,
+                UserManager.ENSURE_VERIFY_APPS
+        ), false /* isDeviceOwner */, UserManagerInternal.CAMERA_NOT_DISABLED, global, local);
+
+        assertRestrictions(newRestrictions(
+                // This one is global no matter who sets it.
+                UserManager.ENSURE_VERIFY_APPS
+        ), global);
+
+        assertRestrictions(newRestrictions(
+                // These can be set by PO too, but when PO sets them, they're local.
+                UserManager.DISALLOW_ADJUST_VOLUME,
+                UserManager.DISALLOW_UNMUTE_MICROPHONE,
+
+                // They can be set by both DO/PO.
+                UserManager.DISALLOW_OUTGOING_BEAM,
+                UserManager.DISALLOW_APPS_CONTROL,
+
+                // These can only be set by DO.
+                UserManager.DISALLOW_USB_FILE_TRANSFER,
+                UserManager.DISALLOW_CONFIG_TETHERING
+        ), local);
+
+    }
+
+    public void testSortToLocalAndGlobalWithCameraDisabled() {
+        final Bundle local = new Bundle();
+        final Bundle global = new Bundle();
+
+        UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, false,
+                UserManagerInternal.CAMERA_DISABLED_GLOBALLY, global, local);
+        assertRestrictions(newRestrictions(UserManager.DISALLOW_CAMERA), global);
+        assertEquals(0, local.size());
+        global.clear();
+
+        UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, false,
+                UserManagerInternal.CAMERA_DISABLED_LOCALLY, global, local);
+        assertEquals(0, global.size());
+        assertRestrictions(newRestrictions(UserManager.DISALLOW_CAMERA), local);
+    }
+
+    public void testMergeAll() {
+        SparseArray<Bundle> restrictions = new SparseArray<>();
+        assertNull(UserRestrictionsUtils.mergeAll(restrictions));
+
+        restrictions.put(0, newRestrictions(UserManager.DISALLOW_ADJUST_VOLUME));
+        restrictions.put(1, newRestrictions(UserManager.DISALLOW_USB_FILE_TRANSFER));
+        restrictions.put(2, newRestrictions(UserManager.DISALLOW_APPS_CONTROL));
+
+        Bundle result = UserRestrictionsUtils.mergeAll(restrictions);
+        assertRestrictions(
+                newRestrictions(
+                        UserManager.DISALLOW_ADJUST_VOLUME,
+                        UserManager.DISALLOW_USB_FILE_TRANSFER,
+                        UserManager.DISALLOW_APPS_CONTROL),
+                result);
+    }
+
+    public void testMoveRestriction() {
+        SparseArray<Bundle> localRestrictions = new SparseArray<>();
+        SparseArray<Bundle> globalRestrictions = new SparseArray<>();
+
+        // User 0 has only local restrictions, nothing should change.
+        localRestrictions.put(0, newRestrictions(UserManager.DISALLOW_ADJUST_VOLUME));
+        // User 1 has a local restriction to be moved to global and some global already. Local
+        // restrictions should be removed for this user.
+        localRestrictions.put(1, newRestrictions(UserManager.ENSURE_VERIFY_APPS));
+        globalRestrictions.put(1, newRestrictions(UserManager.DISALLOW_ADD_USER));
+        // User 2 has a local restriction to be moved and one to leave local.
+        localRestrictions.put(2, newRestrictions(
+                UserManager.ENSURE_VERIFY_APPS,
+                UserManager.DISALLOW_CONFIG_VPN));
+
+        UserRestrictionsUtils.moveRestriction(
+                UserManager.ENSURE_VERIFY_APPS, localRestrictions, globalRestrictions);
+
+        // Check user 0.
+        assertRestrictions(
+                newRestrictions(UserManager.DISALLOW_ADJUST_VOLUME),
+                localRestrictions.get(0));
+        assertNull(globalRestrictions.get(0));
+
+        // Check user 1.
+        assertNull(localRestrictions.get(1));
+        assertRestrictions(
+                newRestrictions(UserManager.ENSURE_VERIFY_APPS, UserManager.DISALLOW_ADD_USER),
+                globalRestrictions.get(1));
+
+        // Check user 2.
+        assertRestrictions(
+                newRestrictions(UserManager.DISALLOW_CONFIG_VPN),
+                localRestrictions.get(2));
+        assertRestrictions(
+                newRestrictions(UserManager.ENSURE_VERIFY_APPS),
+                globalRestrictions.get(2));
     }
 
     public void testAreEqual() {
@@ -172,33 +288,33 @@
 
         assertFalse(UserRestrictionsUtils.areEqual(
                 null,
-                DpmTestUtils.newRestrictions("a")));
+                newRestrictions("a")));
 
         assertFalse(UserRestrictionsUtils.areEqual(
-                DpmTestUtils.newRestrictions("a"),
+                newRestrictions("a"),
                 null));
 
         assertTrue(UserRestrictionsUtils.areEqual(
-                DpmTestUtils.newRestrictions("a"),
-                DpmTestUtils.newRestrictions("a")));
+                newRestrictions("a"),
+                newRestrictions("a")));
 
         assertFalse(UserRestrictionsUtils.areEqual(
-                DpmTestUtils.newRestrictions("a"),
-                DpmTestUtils.newRestrictions("a", "b")));
+                newRestrictions("a"),
+                newRestrictions("a", "b")));
 
         assertFalse(UserRestrictionsUtils.areEqual(
-                DpmTestUtils.newRestrictions("a", "b"),
-                DpmTestUtils.newRestrictions("a")));
+                newRestrictions("a", "b"),
+                newRestrictions("a")));
 
         assertFalse(UserRestrictionsUtils.areEqual(
-                DpmTestUtils.newRestrictions("b", "a"),
-                DpmTestUtils.newRestrictions("a", "a")));
+                newRestrictions("b", "a"),
+                newRestrictions("a", "a")));
 
         // Make sure false restrictions are handled correctly.
-        final Bundle a = DpmTestUtils.newRestrictions("a");
+        final Bundle a = newRestrictions("a");
         a.putBoolean("b", true);
 
-        final Bundle b = DpmTestUtils.newRestrictions("a");
+        final Bundle b = newRestrictions("a");
         b.putBoolean("b", false);
 
         assertFalse(UserRestrictionsUtils.areEqual(a, b));
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index d2512ac..83a61ca 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.ContentObserver;
 import android.webkit.WebViewProviderInfo;
 
 import java.util.HashMap;
@@ -31,6 +30,7 @@
     private boolean mFallbackLogicEnabled;
     private final int mNumRelros;
     private final boolean mIsDebuggable;
+    private int mMultiProcessSetting;
 
     public TestSystemImpl(WebViewProviderInfo[] packageConfigs, boolean fallbackLogicEnabled,
             int numRelros, boolean isDebuggable) {
@@ -121,8 +121,15 @@
     }
 
     @Override
-    public void setMultiProcessEnabledFromContext(Context context) {}
+    public int getMultiProcessSetting(Context context) {
+        return mMultiProcessSetting;
+    }
 
     @Override
-    public void registerContentObserver(Context context, ContentObserver contentObserver) {}
+    public void setMultiProcessSetting(Context context, int value) {
+        mMultiProcessSetting = value;
+    }
+
+    @Override
+    public void notifyZygote(boolean enableMultiProcess) {}
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
new file mode 100644
index 0000000..48d4770
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE;
+import static android.graphics.GraphicBuffer.USAGE_SW_READ_NEVER;
+import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_NEVER;
+import static android.graphics.GraphicBuffer.create;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import android.app.ActivityManager.TaskSnapshot;
+import android.content.res.Configuration;
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test class for {@link TaskSnapshotCache}.
+ *
+ * runtest frameworks-services -c com.android.server.wm.TaskSnapshotCacheTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class TaskSnapshotCacheTest extends WindowTestsBase {
+
+    @Test
+    public void testCleanCache() throws Exception {
+        TaskSnapshotCache snapshotCache = new TaskSnapshotCache();
+        final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+        snapshotCache.putSnapshot(window.getTask(), createSnapshot());
+        assertNotNull(snapshotCache.getSnapshot(window.getTask()));
+        snapshotCache.cleanCache(window.mAppToken);
+        assertNull(snapshotCache.getSnapshot(window.getTask()));
+    }
+
+    private TaskSnapshot createSnapshot() {
+        GraphicBuffer buffer = create(1, 1, PixelFormat.RGBA_8888,
+                USAGE_HW_TEXTURE | USAGE_SW_WRITE_NEVER | USAGE_SW_READ_NEVER);
+        return new TaskSnapshot(buffer, Configuration.ORIENTATION_PORTRAIT, new Rect());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 44d5055..dd5077b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -23,6 +23,8 @@
 import org.junit.Assert;
 import org.junit.Before;
 
+import android.app.ActivityManager;
+import android.app.ActivityManager.TaskSnapshot;
 import android.content.Context;
 import android.os.IBinder;
 import android.support.test.InstrumentationRegistry;
@@ -238,7 +240,7 @@
         }
 
         TestTaskWindowContainerController(int stackId) {
-            super(sNextTaskId++, stackId, 0 /* userId */, null /* bounds */,
+            super(sNextTaskId++, snapshot -> {}, stackId, 0 /* userId */, null /* bounds */,
                     EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE, false /* homeTask*/,
                     false /* isOnTopLauncher */, true /* toTop*/, true /* showForAllUsers */);
         }
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index b2a9a49..00420e9 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -114,6 +116,9 @@
         mUNM.registerMobileNetworkRequest();
         assertTrue(mUNM.mobileNetworkRequested());
         assertEquals(1, mCM.requested.size());
+        assertEquals(1, mCM.legacyTypeMap.size());
+        assertEquals(Integer.valueOf(TYPE_MOBILE_HIPRI),
+                mCM.legacyTypeMap.values().iterator().next());
         assertFalse(mCM.isDunRequested());
 
         mUNM.stop();
@@ -137,6 +142,9 @@
         mUNM.registerMobileNetworkRequest();
         assertTrue(mUNM.mobileNetworkRequested());
         assertEquals(1, mCM.requested.size());
+        assertEquals(1, mCM.legacyTypeMap.size());
+        assertEquals(Integer.valueOf(TYPE_MOBILE_DUN),
+                mCM.legacyTypeMap.values().iterator().next());
         assertTrue(mCM.isDunRequested());
 
         mUNM.stop();
@@ -148,6 +156,7 @@
         public Set<NetworkCallback> trackingDefault = new HashSet<>();
         public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
         public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
+        public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>();
 
         public TestConnectivityManager(Context ctx, IConnectivityManager svc) {
             super(ctx, svc);
@@ -156,7 +165,8 @@
         boolean isEmpty() {
             return trackingDefault.isEmpty() &&
                    listening.isEmpty() &&
-                   requested.isEmpty();
+                   requested.isEmpty() &&
+                   legacyTypeMap.isEmpty();
         }
 
         boolean isListeningForDun() {
@@ -184,6 +194,17 @@
         }
 
         @Override
+        public void requestNetwork(NetworkRequest req, NetworkCallback cb,
+                int timeoutMs, int legacyType) {
+            assertFalse(requested.containsKey(cb));
+            requested.put(cb, req);
+            assertFalse(legacyTypeMap.containsKey(cb));
+            if (legacyType != ConnectivityManager.TYPE_NONE) {
+                legacyTypeMap.put(cb, legacyType);
+            }
+        }
+
+        @Override
         public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
             assertFalse(listening.containsKey(cb));
             listening.put(cb, req);
@@ -203,6 +224,7 @@
                 listening.remove(cb);
             } else if (requested.containsKey(cb)) {
                 requested.remove(cb);
+                legacyTypeMap.remove(cb);
             }
 
             assertFalse(trackingDefault.contains(cb));
diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py
index b071093..9dceba2 100755
--- a/tools/localedata/extract_icu_data.py
+++ b/tools/localedata/extract_icu_data.py
@@ -48,6 +48,8 @@
             # they may be used by apps for other purposes.)
             "en_XA": "~~~A",
             "ar_XB": "~~~B",
+            # Removed data from later versions of ICU
+            "ji": "Hebr", # Old code for Yiddish, still used in Java and Android
         }
         representative_locales = {
             # Android's additions
@@ -69,7 +71,7 @@
                 _, to_scr, to_region = get_locale_parts(to_locale)
                 if from_lang == 'und':
                     continue  # not very useful for our purposes
-                if from_region is None and to_region != '001':
+                if from_region is None and to_region not in ['001', 'ZZ']:
                     representative_locales.add(to_locale)
                 if from_scr is None:
                     likely_script_dict[from_locale] = to_scr